1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <alloca.h>
28 #include <stdarg.h>
29 #include <sys/types.h>
30 #include <sys/sysmacros.h>
31 #include <assert.h>
32 #include <sys/socket.h>
33 #include <sys/byteorder.h>
34 #include <string.h>
35 #include <time.h>
36 #include <sys/socket.h>
37 #include <syslog.h>
38 #include <sys/errno.h>
39 #include <net/if.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
42 #include <netdb.h>
43 #include <netinet/dhcp.h>
44 #include <dhcp_symbol.h>
45 #include <nss_dbdefs.h>
46 #include <dlfcn.h>
47 #include "dhcpd.h"
48 #include "per_dnet.h"
49 #include "interfaces.h"
50 #include <locale.h>
51 #include <resolv.h>
52
53 static void dhcp_offer(dsvc_clnt_t *, PKT_LIST *);
54 static void dhcp_req_ack(dsvc_clnt_t *, PKT_LIST *);
55 static void dhcp_dec_rel(dsvc_clnt_t *, PKT_LIST *, int);
56 static void dhcp_inform(dsvc_clnt_t *, PKT_LIST *);
57 static PKT *gen_reply_pkt(dsvc_clnt_t *, PKT_LIST *, int, uint_t *,
58 uchar_t **, struct in_addr *);
59 static void set_lease_option(ENCODE **, lease_t);
60 static int config_lease(PKT_LIST *, dn_rec_t *, ENCODE **, lease_t, boolean_t);
61 static int is_option_requested(PKT_LIST *, ushort_t);
62 static void add_request_list(IF *, PKT_LIST *, ENCODE **, struct in_addr *);
63 static char *disp_clnt_msg(PKT_LIST *, char *, int);
64 static void add_dnet_cache(dsvc_dnet_t *, dn_rec_list_t *);
65 static void purge_dnet_cache(dsvc_dnet_t *, dn_rec_t *);
66
67 static boolean_t addr_avail(dsvc_dnet_t *, dsvc_clnt_t *, dn_rec_list_t **,
68 struct in_addr, boolean_t);
69 static boolean_t name_avail(char *, dsvc_clnt_t *, PKT_LIST *,
70 dn_rec_list_t **, ENCODE *, struct in_addr **);
71 static boolean_t entry_available(dsvc_clnt_t *, dn_rec_t *);
72 static boolean_t do_nsupdate(struct in_addr, ENCODE *, PKT_LIST *);
73
74 extern int dns_puthostent(struct hostent *, time_t);
75
76 /*
77 * Offer cache.
78 *
79 * The DHCP server maintains a cache of DHCP OFFERs it has extended to DHCP
80 * clients. It does so because:
81 * a) Subsequent requests get the same answer, and the same IP address
82 * isn't offered to a different client.
83 *
84 * b) No ICMP validation is required the second time through, nor is a
85 * database lookup required.
86 *
87 * c) If the client accepts the OFFER and sends a REQUEST, we can simply
88 * lookup the record by client IP address, the one field guaranteed to
89 * be unique within the dhcp network table.
90 *
91 * We don't explicitly delete entries from the offer cache. We let them time
92 * out on their own. This is done to ensure the server responds correctly when
93 * many pending client requests are queued (duplicates). We don't want to ICMP
94 * validate an IP address we just allocated.
95 *
96 * The offer cache (and any database records cached in select_offer()) will
97 * diverge from the database for the length of the D_OFFER lifetime.
98 * SIGHUP flushes the offer cache, allowing management tools to inform the
99 * server of changes in a timely manner.
100 */
101
102 /*
103 * Dispatch the DHCP packet based on its type.
104 */
105 void
106 dhcp(dsvc_clnt_t *pcd, PKT_LIST *plp)
107 {
108 if (plp->opts[CD_DHCP_TYPE]->len != 1) {
109 dhcpmsg(LOG_ERR,
110 "Garbled DHCP Message type option from client: %s\n",
111 pcd->cidbuf);
112 return;
113 }
114
115 pcd->state = *plp->opts[CD_DHCP_TYPE]->value;
116 switch (pcd->state) {
117 case DISCOVER:
118 #ifdef DEBUG
119 dhcpmsg(LOG_DEBUG, "dhcp() - processing OFFER...\n");
120 #endif /* DEBUG */
121 dhcp_offer(pcd, plp);
122 #ifdef DEBUG
123 dhcpmsg(LOG_DEBUG, "dhcp() - processed OFFER.\n");
124 #endif /* DEBUG */
125 break;
126 case REQUEST:
127 #ifdef DEBUG
128 dhcpmsg(LOG_DEBUG, "dhcp() - processing REQUEST...\n");
129 #endif /* DEBUG */
130 dhcp_req_ack(pcd, plp);
131 #ifdef DEBUG
132 dhcpmsg(LOG_DEBUG, "dhcp() - processed REQUEST.\n");
133 #endif /* DEBUG */
134 break;
135 case DECLINE:
136 #ifdef DEBUG
137 dhcpmsg(LOG_DEBUG, "dhcp() - processing DECLINE...\n");
138 #endif /* DEBUG */
139 dhcp_dec_rel(pcd, plp, DECLINE);
140 #ifdef DEBUG
141 dhcpmsg(LOG_DEBUG, "dhcp() - processed DECLINE.\n");
142 #endif /* DEBUG */
143 break;
144 case RELEASE:
145 #ifdef DEBUG
146 dhcpmsg(LOG_DEBUG, "dhcp() - processing RELEASE...\n");
147 #endif /* DEBUG */
148 dhcp_dec_rel(pcd, plp, RELEASE);
149 #ifdef DEBUG
150 dhcpmsg(LOG_DEBUG, "dhcp() - processed RELEASE.\n");
151 #endif /* DEBUG */
152 break;
153 case INFORM:
154 #ifdef DEBUG
155 dhcpmsg(LOG_DEBUG, "dhcp() - processing INFORM...\n");
156 #endif /* DEBUG */
157 dhcp_inform(pcd, plp);
158 #ifdef DEBUG
159 dhcpmsg(LOG_DEBUG, "dhcp() - processed INFORM.\n");
160 #endif /* DEBUG */
161 break;
162 default:
163 dhcpmsg(LOG_INFO,
164 "Unexpected DHCP message type: %d from client: %s.\n",
165 pcd->state, pcd->cidbuf);
166 break;
167 }
168 }
169
170 /*
171 * Responding to a DISCOVER message. icmp echo check (if done) is synchronous.
172 * Previously known requests are in the OFFER cache.
173 */
174 static void
175 dhcp_offer(dsvc_clnt_t *pcd, PKT_LIST *plp)
176 {
177 IF *ifp = pcd->ifp;
178 boolean_t result;
179 struct in_addr nsip, ncip;
180 dsvc_dnet_t *pnd = pcd->pnd;
181 uint_t replen;
182 int used_pkt_len;
183 PKT *rep_pktp = NULL;
184 uchar_t *optp;
185 ENCODE *ecp, *vecp, *macro_ecp, *macro_vecp,
186 *class_ecp, *class_vecp,
187 *cid_ecp, *cid_vecp,
188 *net_ecp, *net_vecp;
189 MACRO *net_mp, *pkt_mp, *class_mp, *cid_mp;
190 char *class_id;
191 time_t now = time(NULL);
192 lease_t newlease, oldlease = 0;
193 int err = 0;
194 boolean_t existing_allocation = B_FALSE;
195 boolean_t existing_offer = B_FALSE;
196 char sipstr[INET_ADDRSTRLEN], cipstr[INET_ADDRSTRLEN];
197 char class_idbuf[DSYM_CLASS_SIZE];
198 dn_rec_t *dnp, dn, ndn;
199 uint32_t query;
200 dn_rec_list_t *dncp = NULL, *dnlp = NULL;
201 boolean_t unreserve = B_FALSE;
202
203 class_id = get_class_id(plp, class_idbuf, sizeof (class_idbuf));
204
205 /*
206 * Purge offers when expired or the database has been re-read.
207 *
208 * Multi-threading: to better distribute garbage collection
209 * and data structure aging tasks, each thread must actively
210 * implement policy, rather then specialized, non-scalable
211 * threads which halt the server and update all data
212 * structures.
213 *
214 * The test below checks whether the offer has expired,
215 * due to aging, or re-reading of the dhcptab, via timeout
216 * or explicit signal.
217 */
218 if (pcd->off_ip.s_addr != htonl(INADDR_ANY) &&
219 PCD_OFFER_TIMEOUT(pcd, now))
220 purge_offer(pcd, B_TRUE, B_TRUE);
221
222 if (pcd->off_ip.s_addr != htonl(INADDR_ANY)) {
223 /*
224 * We've already validated this IP address in the past, and
225 * due to the OFFER hash table, we would not have offered this
226 * IP address to another client, so use the offer-cached record.
227 */
228 existing_offer = B_TRUE;
229 dnlp = pcd->dnlp;
230 dnp = dnlp->dnl_rec;
231 ncip.s_addr = htonl(dnp->dn_cip.s_addr);
232 } else {
233 /* Try to find an existing usable entry for the client. */
234 DSVC_QINIT(query);
235 DSVC_QEQ(query, DN_QCID);
236 (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
237 dn.dn_cid_len = pcd->cid_len;
238
239 /* No bootp records, thank you. */
240 DSVC_QNEQ(query, DN_QFBOOTP_ONLY);
241 dn.dn_flags = DN_FBOOTP_ONLY;
242
243 /*
244 * We don't limit this search by SIP, because this client
245 * may be owned by another server, and we need to detect this
246 * since that record may be MANUAL.
247 */
248 dncp = NULL;
249 dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
250 -1, &dn, (void **)&dncp, S_CID);
251
252 while (dnlp != NULL) {
253
254 dnp = dnlp->dnl_rec;
255 if (match_ownerip(htonl(dnp->dn_sip.s_addr)) == NULL) {
256 /*
257 * An IP address, but not ours! It's up to the
258 * primary to respond to DISCOVERs on this
259 * address.
260 */
261 if (verbose) {
262 char *m1, *m2;
263
264 if (dnp->dn_flags & DN_FMANUAL) {
265 m1 = "MANUAL";
266 m2 = " No other IP address "
267 "will be allocated.";
268 } else {
269 m1 = "DYNAMIC";
270 m2 = "";
271 }
272
273 nsip.s_addr = htonl(dnp->dn_sip.s_addr);
274 (void) inet_ntop(AF_INET, &nsip, sipstr,
275 sizeof (sipstr));
276 ncip.s_addr = htonl(dnp->dn_cip.s_addr);
277 (void) inet_ntop(AF_INET, &ncip, cipstr,
278 sizeof (cipstr));
279 dhcpmsg(LOG_INFO, "Client: %1$s has "
280 "%2$s %3$s owned by server: "
281 "%4$s.%5$s\n", pcd->cidbuf,
282 m1, cipstr, sipstr, m2);
283 }
284
285 /* We give up if that IP address is manual */
286 if (dnp->dn_flags & DN_FMANUAL)
287 goto leave_offer;
288 } else {
289 uint_t bits = DN_FUNUSABLE | DN_FMANUAL;
290 if ((dnp->dn_flags & bits) == bits) {
291 ncip.s_addr = htonl(dnp->dn_cip.s_addr);
292 (void) inet_ntop(AF_INET, &ncip, cipstr,
293 sizeof (cipstr));
294 dhcpmsg(LOG_WARNING, "Client: %1$s "
295 "MANUAL record %2$s is UNUSABLE. "
296 "No other IP address will be "
297 "allocated.\n", pcd->cidbuf,
298 cipstr);
299 goto leave_offer;
300 } else
301 break; /* success */
302 }
303
304 free_dnrec_list(dnlp);
305 dnlp = detach_dnrec_from_list(NULL, dncp, &dncp);
306 }
307
308 if (dnlp == NULL) {
309 /*
310 * select_offer() ONLY selects IP addresses owned
311 * by us. Only log a notice if we own any IP addresses
312 * at all. Otherwise, this is an informational server.
313 */
314 if (!select_offer(pnd, plp, pcd, &dnlp)) {
315 if (pnd->naddrs > 0) {
316 dhcpmsg(LOG_NOTICE,
317 "No more IP addresses on %1$s "
318 "network (%2$s)\n", pnd->network,
319 pcd->cidbuf);
320 }
321 goto leave_offer;
322 }
323 dnp = dnlp->dnl_rec;
324 } else
325 existing_allocation = B_TRUE;
326
327 ncip.s_addr = htonl(dnp->dn_cip.s_addr);
328 (void) inet_ntop(AF_INET, &ncip, cipstr, sizeof (cipstr));
329
330 /*
331 * ICMP echo validate the address.
332 */
333 if (!noping) {
334 /*
335 * If icmp echo validation fails, let the plp fall by
336 * the wayside.
337 */
338 if (icmp_echo_check(&ncip, &result) != 0) {
339 dhcpmsg(LOG_ERR, "ICMP ECHO check cannot be "
340 "registered for: %s, ignoring\n", cipstr);
341 unreserve = B_TRUE;
342 goto leave_offer;
343 }
344 if (result) {
345 dhcpmsg(LOG_WARNING,
346 "ICMP ECHO reply to OFFER candidate: "
347 "%s, disabling.\n", cipstr);
348
349 ndn = *dnp; /* struct copy */
350 ndn.dn_flags |= DN_FUNUSABLE;
351
352 if ((err = dhcp_modify_dd_entry(pnd->dh, dnp,
353 &ndn)) != DSVC_SUCCESS) {
354 dhcpmsg(LOG_ERR,
355 "ICMP ECHO reply to OFFER "
356 "candidate: %1$s. No "
357 "modifiable dhcp network "
358 "record. (%2$s)\n", cipstr,
359 dhcpsvc_errmsg(err));
360 } else {
361 /* Keep the cached entry current. */
362 *dnp = ndn; /* struct copy */
363 }
364
365 logtrans(P_DHCP, L_ICMP_ECHO, 0, ncip,
366 server_ip, plp);
367
368 unreserve = B_TRUE;
369
370 goto leave_offer;
371 }
372 }
373 }
374
375 /*
376 * At this point, we've ICMP validated (if requested) the IP
377 * address, and can go about producing an OFFER for the client.
378 */
379
380 ecp = vecp = NULL;
381 net_vecp = net_ecp = NULL;
382 macro_vecp = macro_ecp = NULL;
383 class_vecp = class_ecp = NULL;
384 cid_vecp = cid_ecp = NULL;
385 if (!no_dhcptab) {
386 open_macros();
387
388 /*
389 * Macros are evaluated this way: First apply parameters from
390 * a client class macro (if present), then apply those from the
391 * network macro (if present), then apply those from the
392 * dhcp network macro (if present), and finally apply those
393 * from a client id macro (if present).
394 */
395
396 /*
397 * First get a handle on network, dhcp network table macro,
398 * and client id macro values.
399 */
400 if ((net_mp = get_macro(pnd->network)) != NULL)
401 net_ecp = net_mp->head;
402 if ((pkt_mp = get_macro(dnp->dn_macro)) != NULL)
403 macro_ecp = pkt_mp->head;
404 if ((cid_mp = get_macro(pcd->cidbuf)) != NULL)
405 cid_ecp = cid_mp->head;
406
407 if (class_id != NULL) {
408 /* Get a handle on the class id macro (if it exists). */
409 if ((class_mp = get_macro(class_id)) != NULL) {
410 /*
411 * Locate the ENCODE list for encapsulated
412 * options associated with our class id within
413 * the class id macro.
414 */
415 class_vecp = vendor_encodes(class_mp, class_id);
416 class_ecp = class_mp->head;
417 }
418
419 /*
420 * Locate the ENCODE list for encapsulated options
421 * associated with our class id within the network,
422 * dhcp network, and client macros.
423 */
424 if (net_mp != NULL)
425 net_vecp = vendor_encodes(net_mp, class_id);
426 if (pkt_mp != NULL)
427 macro_vecp = vendor_encodes(pkt_mp, class_id);
428 if (cid_mp != NULL)
429 cid_vecp = vendor_encodes(cid_mp, class_id);
430
431 /*
432 * Combine the encapsulated option encode lists
433 * associated with our class id in the order defined
434 * above (class, net, dhcp network, client id)
435 */
436 vecp = combine_encodes(class_vecp, net_vecp, ENC_COPY);
437 vecp = combine_encodes(vecp, macro_vecp, ENC_DONT_COPY);
438 vecp = combine_encodes(vecp, cid_vecp, ENC_DONT_COPY);
439 }
440
441 /*
442 * Combine standard option encode lists in the order defined
443 * above (class, net, dhcp network, and client id).
444 */
445 if (class_ecp != NULL)
446 ecp = combine_encodes(class_ecp, net_ecp, ENC_COPY);
447 else
448 ecp = dup_encode_list(net_ecp);
449
450 ecp = combine_encodes(ecp, macro_ecp, ENC_DONT_COPY);
451 ecp = combine_encodes(ecp, cid_ecp, ENC_DONT_COPY);
452
453 /* If dhcptab configured to return hostname, do so. */
454 if (find_encode(ecp, DSYM_INTERNAL, CD_BOOL_HOSTNAME) != NULL) {
455 struct hostent h, *hp;
456 char hbuf[NSS_BUFLEN_HOSTS];
457 ENCODE *hecp;
458 hp = gethostbyaddr_r((char *)&ncip, sizeof (ncip),
459 AF_INET, &h, hbuf, sizeof (hbuf), &err);
460 if (hp != NULL) {
461 hecp = make_encode(DSYM_STANDARD,
462 CD_HOSTNAME, strlen(hp->h_name),
463 hp->h_name, ENC_COPY);
464 replace_encode(&ecp, hecp, ENC_DONT_COPY);
465 }
466 }
467
468 /* If dhcptab configured to echo client class, do so. */
469 if (plp->opts[CD_CLASS_ID] != NULL &&
470 find_encode(ecp, DSYM_INTERNAL, CD_BOOL_ECHO_VCLASS) !=
471 NULL) {
472 ENCODE *echo_ecp;
473 DHCP_OPT *op = plp->opts[CD_CLASS_ID];
474 echo_ecp = make_encode(DSYM_STANDARD, CD_CLASS_ID,
475 op->len, op->value, ENC_COPY);
476 replace_encode(&ecp, echo_ecp, ENC_DONT_COPY);
477 }
478 }
479
480 if ((ifp->flags & IFF_NOARP) == 0)
481 (void) set_arp(ifp, &ncip, NULL, 0, DHCP_ARP_DEL);
482
483 /*
484 * For OFFERs, we don't check the client's lease nor LeaseNeg,
485 * regardless of whether the client has an existing allocation
486 * or not. Lease expiration (w/o LeaseNeg) only occur during
487 * RENEW/REBIND or INIT-REBOOT client states, not SELECTing state.
488 */
489 if (existing_allocation) {
490 if (dnp->dn_lease == DHCP_PERM ||
491 (dnp->dn_flags & DN_FAUTOMATIC)) {
492 oldlease = DHCP_PERM;
493 } else {
494 if ((lease_t)dnp->dn_lease < (lease_t)now)
495 oldlease = (lease_t)0;
496 else {
497 oldlease = (lease_t)dnp->dn_lease -
498 (lease_t)now;
499 }
500 }
501 }
502
503 /* First get a generic reply packet. */
504 rep_pktp = gen_reply_pkt(pcd, plp, OFFER, &replen, &optp, &ifp->addr);
505
506 /* Set the client's IP address */
507 rep_pktp->yiaddr.s_addr = htonl(dnp->dn_cip.s_addr);
508
509 /* Calculate lease time. */
510 newlease = config_lease(plp, dnp, &ecp, oldlease, B_TRUE);
511
512 /*
513 * Client is requesting specific options. let's try and ensure it
514 * gets what it wants, if at all possible.
515 */
516 if (plp->opts[CD_REQUEST_LIST] != NULL)
517 add_request_list(ifp, plp, &ecp, &ncip);
518
519 /* Now load all the asked for / configured options */
520 used_pkt_len = load_options(DHCP_DHCP_CLNT | DHCP_SEND_LEASE, plp,
521 rep_pktp, replen, optp, ecp, vecp);
522
523 free_encode_list(ecp);
524 free_encode_list(vecp);
525 if (!no_dhcptab)
526 close_macros();
527
528 if (used_pkt_len < sizeof (PKT))
529 used_pkt_len = sizeof (PKT);
530
531 if (send_reply(ifp, rep_pktp, used_pkt_len, &ncip) == 0) {
532 if (newlease == DHCP_PERM)
533 newlease = htonl(newlease);
534 else
535 newlease = htonl(now + newlease);
536 (void) update_offer(pcd, &dnlp, newlease, NULL, B_TRUE);
537 existing_offer = B_TRUE;
538 } else {
539 unreserve = B_TRUE;
540 }
541
542 leave_offer:
543 if (unreserve)
544 purge_offer(pcd, B_FALSE, B_TRUE);
545 if (rep_pktp != NULL)
546 free(rep_pktp);
547 if (dncp != NULL)
548 dhcp_free_dd_list(pnd->dh, dncp);
549 if (dnlp != NULL && !existing_offer)
550 dhcp_free_dd_list(pnd->dh, dnlp);
551 }
552
553 /*
554 * Responding to REQUEST message.
555 *
556 * Very similar to dhcp_offer(), except that we need to be more
557 * discriminating.
558 *
559 * The ciaddr field is TRUSTED. A INIT-REBOOTing client will place its
560 * notion of its IP address in the requested IP address option. INIT
561 * clients will place the value in the OFFERs yiaddr in the requested
562 * IP address option. INIT-REBOOT packets are differentiated from INIT
563 * packets in that the server id option is missing. ciaddr will only
564 * appear from clients in the RENEW/REBIND states.
565 *
566 * Error messages may be generated. Database write failures are no longer
567 * fatal, since we'll only respond to the client if the write succeeds.
568 */
569 static void
570 dhcp_req_ack(dsvc_clnt_t *pcd, PKT_LIST *plp)
571 {
572 dn_rec_t dn, ndn, *dnp;
573 struct in_addr serverid, ciaddr, claddr, nreqaddr, cipaddr,
574 ncipaddr, sipaddr;
575 struct in_addr dest_in;
576 dsvc_dnet_t *pnd = pcd->pnd;
577 uint_t replen;
578 int actual_len;
579 int pkt_type = ACK;
580 DHCP_MSG_CATEGORIES log;
581 PKT *rep_pktp = NULL;
582 uchar_t *optp;
583 ENCODE *ecp, *vecp,
584 *class_ecp, *class_vecp,
585 *net_ecp, *net_vecp,
586 *macro_ecp, *macro_vecp,
587 *cid_ecp, *cid_vecp;
588 MACRO *class_mp, *pkt_mp, *net_mp, *cid_mp;
589 char *class_id;
590 char nak_mesg[DHCP_SCRATCH];
591 time_t now;
592 lease_t newlease, oldlease;
593 boolean_t negot;
594 int err = 0;
595 int write_error = DSVC_SUCCESS, clnt_state;
596 ushort_t boot_secs;
597 char ntoaa[INET_ADDRSTRLEN], ntoab[INET_ADDRSTRLEN],
598 ntoac[INET_ADDRSTRLEN];
599 char class_idbuf[DSYM_CLASS_SIZE];
600 boolean_t hostname_update = B_FALSE;
601 dn_rec_list_t *nlp, *dncp = NULL, *dnlp = NULL;
602 uint32_t query;
603 IF *ifp = pcd->ifp;
604 boolean_t existing_offer = B_FALSE;
605
606 ciaddr.s_addr = plp->pkt->ciaddr.s_addr;
607 boot_secs = ntohs(plp->pkt->secs);
608 now = time(NULL);
609
610 class_id = get_class_id(plp, class_idbuf, sizeof (class_idbuf));
611
612 /* Determine type of REQUEST we've got. */
613 if (plp->opts[CD_SERVER_ID] != NULL) {
614 if (plp->opts[CD_SERVER_ID]->len != sizeof (struct in_addr)) {
615 dhcpmsg(LOG_ERR, "Garbled DHCP Server ID option from "
616 "client: '%1$s'. Len is %2$d, when it should be "
617 "%3$d \n", pcd->cidbuf,
618 plp->opts[CD_SERVER_ID]->len,
619 sizeof (struct in_addr));
620 goto leave_ack;
621 }
622
623 /*
624 * Request in response to an OFFER. ciaddr must not
625 * be set. Requested IP address option will hold address
626 * we offered the client.
627 */
628 clnt_state = INIT_STATE;
629 (void) memcpy((void *)&serverid,
630 plp->opts[CD_SERVER_ID]->value, sizeof (struct in_addr));
631
632 if (plp->opts[CD_REQUESTED_IP_ADDR] == NULL) {
633 if (verbose) {
634 dhcpmsg(LOG_NOTICE, "%1$s: REQUEST on %2$s is "
635 "missing requested IP option.\n",
636 pcd->cidbuf, pcd->pnd->network);
637 }
638 goto leave_ack;
639 }
640 if (plp->opts[CD_REQUESTED_IP_ADDR]->len !=
641 sizeof (struct in_addr)) {
642 dhcpmsg(LOG_ERR, "Garbled Requested IP option from "
643 "client: '%1$s'. Len is %2$d, when it should be "
644 "%3$d \n",
645 pcd->cidbuf, plp->opts[CD_REQUESTED_IP_ADDR]->len,
646 sizeof (struct in_addr));
647 goto leave_ack;
648 }
649 (void) memcpy((void *)&nreqaddr,
650 plp->opts[CD_REQUESTED_IP_ADDR]->value,
651 sizeof (struct in_addr));
652
653 if (serverid.s_addr != ifp->addr.s_addr) {
654 /*
655 * Another server address was selected.
656 *
657 * If the server address is handled by another
658 * thread of our process, do nothing in the
659 * hope that the other thread will eventually
660 * receive a REQUEST with its server address.
661 *
662 * If a server address was selected which is
663 * not handled by this process, see if we made
664 * an offer, and clear it if we did. If offer
665 * expired before client responded, then no
666 * need to do anything.
667 */
668 if (is_our_address(serverid.s_addr)) {
669 if (verbose) {
670 dhcpmsg(LOG_INFO,
671 "Client: %1$s chose %2$s from "
672 "server: %3$s, which is being "
673 "handled by another thread\n",
674 pcd->cidbuf,
675 inet_ntop(AF_INET, &nreqaddr, ntoaa,
676 sizeof (ntoaa)),
677 inet_ntop(AF_INET, &serverid, ntoab,
678 sizeof (ntoab)));
679 }
680 } else {
681 purge_offer(pcd, B_FALSE, B_TRUE);
682 if (verbose) {
683 dhcpmsg(LOG_INFO,
684 "Client: %1$s chose %2$s from "
685 "server: %3$s, not %4$s\n",
686 pcd->cidbuf,
687 inet_ntop(AF_INET, &nreqaddr, ntoaa,
688 sizeof (ntoaa)),
689 inet_ntop(AF_INET, &serverid, ntoab,
690 sizeof (ntoab)),
691 inet_ntop(AF_INET, &ifp->addr,
692 ntoac, sizeof (ntoac)));
693 }
694 }
695 goto leave_ack;
696 }
697
698 /*
699 * See comment at the top of the file for description of
700 * OFFER cache.
701 *
702 * If the offer expires before the client got around to
703 * requesting, and we can't confirm the address is still free,
704 * we'll silently ignore the client, until it drops back and
705 * tries to discover again. We will print a message in
706 * verbose mode however. If the Offer hasn't timed out, we
707 * bump it up again in case we have a bounce of queued up
708 * INIT requests to respond to.
709 */
710 if (pcd->off_ip.s_addr == htonl(INADDR_ANY) ||
711 PCD_OFFER_TIMEOUT(pcd, now)) {
712 /*
713 * Hopefully, the timeout value is fairly long to
714 * prevent this.
715 */
716 purge_offer(pcd, B_TRUE, B_TRUE);
717 if (verbose) {
718 dhcpmsg(LOG_INFO,
719 "Offer on %1$s expired for client: %2$s\n",
720 pcd->pnd->network, pcd->cidbuf);
721 }
722 goto leave_ack;
723 } else
724 (void) update_offer(pcd, NULL, 0, NULL, B_TRUE);
725
726 /*
727 * The client selected us. Create a ACK, and send
728 * it off to the client, commit to permanent
729 * storage the new binding.
730 */
731 existing_offer = B_TRUE;
732 dnlp = pcd->dnlp;
733 dnp = dnlp->dnl_rec;
734 ndn = *dnp; /* struct copy */
735 ndn.dn_lease = pcd->lease;
736 ncipaddr.s_addr = htonl(dnp->dn_cip.s_addr);
737
738 /*
739 * If client thinks we offered it a different address, then
740 * ignore it.
741 */
742 if (memcmp((char *)&ncipaddr,
743 plp->opts[CD_REQUESTED_IP_ADDR]->value,
744 sizeof (struct in_addr)) != 0) {
745 if (verbose) {
746 dhcpmsg(LOG_INFO, "Client %1$s believes "
747 "offered IP address %2$s is different than "
748 "what was offered.\n", pcd->cidbuf,
749 inet_ntop(AF_INET, &ncipaddr, ntoab,
750 sizeof (ntoab)));
751 }
752 goto leave_ack;
753 }
754
755 /*
756 * Clear out any temporary ARP table entry we may have
757 * created during the offer.
758 */
759 if ((ifp->flags & IFF_NOARP) == 0)
760 (void) set_arp(ifp, &ncipaddr, NULL, 0, DHCP_ARP_DEL);
761 } else {
762 /*
763 * Either a client in the INIT-REBOOT state, or one in
764 * either RENEW or REBIND states. The latter will have
765 * ciaddr set, whereas the former will place its concept
766 * of its IP address in the requested IP address option.
767 */
768 if (ciaddr.s_addr == htonl(INADDR_ANY)) {
769 clnt_state = INIT_REBOOT_STATE;
770 /*
771 * Client isn't sure of its IP address. It's
772 * attempting to verify its address, thus requested
773 * IP option better be present, and correct.
774 */
775 if (plp->opts[CD_REQUESTED_IP_ADDR] == NULL) {
776 dhcpmsg(LOG_ERR,
777 "Client: %s REQUEST is missing "
778 "requested IP option.\n", pcd->cidbuf);
779 goto leave_ack;
780 }
781 if (plp->opts[CD_REQUESTED_IP_ADDR]->len !=
782 sizeof (struct in_addr)) {
783 dhcpmsg(LOG_ERR, "Garbled Requested IP option "
784 "from client: '%1$s'. Len is %2$d, when it "
785 "should be %3$d \n", pcd->cidbuf,
786 plp->opts[CD_REQUESTED_IP_ADDR]->len,
787 sizeof (struct in_addr));
788 goto leave_ack;
789 }
790 (void) memcpy(&claddr,
791 plp->opts[CD_REQUESTED_IP_ADDR]->value,
792 sizeof (struct in_addr));
793
794 DSVC_QINIT(query);
795 DSVC_QEQ(query, DN_QCID);
796 (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
797 dn.dn_cid_len = pcd->cid_len;
798
799 /* No bootp records, thank you. */
800 DSVC_QNEQ(query, DN_QFBOOTP_ONLY);
801 dn.dn_flags = DN_FBOOTP_ONLY;
802
803 } else {
804 clnt_state = RENEW_REBIND_STATE;
805 /*
806 * Client knows its IP address. It is trying to
807 * RENEW/REBIND (extend its lease). We trust ciaddr,
808 * and use it to locate the client's record. If we
809 * can't find the client's record, then we keep
810 * silent. If the client id of the record doesn't
811 * match this client, then the database is
812 * inconsistent, and we'll ignore it.
813 */
814 DSVC_QINIT(query);
815 DSVC_QEQ(query, DN_QCID|DN_QCIP);
816 (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
817 dn.dn_cid_len = pcd->cid_len;
818 dn.dn_cip.s_addr = ntohl(ciaddr.s_addr);
819
820 /* No bootp records, thank you. */
821 DSVC_QNEQ(query, DN_QFBOOTP_ONLY);
822 dn.dn_flags = DN_FBOOTP_ONLY;
823
824 claddr.s_addr = ciaddr.s_addr;
825 }
826
827 dncp = NULL;
828 dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query,
829 -1, &dn, (void **)&dncp, S_CID);
830
831 if (dnlp != NULL) {
832 dnp = dnlp->dnl_rec;
833 if (dnp->dn_flags & DN_FUNUSABLE)
834 goto leave_ack;
835
836 sipaddr.s_addr = htonl(dnp->dn_sip.s_addr);
837 cipaddr.s_addr = htonl(dnp->dn_cip.s_addr);
838
839 /*
840 * If this address is not owned by this server and
841 * the client is trying to verify the address, then
842 * ignore the client. If the client is simply trying
843 * to rebind, then don't respond until after
844 * renog_secs passes, to give the server that *OWNS*
845 * the address time to respond first.
846 */
847 if (match_ownerip(sipaddr.s_addr) == NULL) {
848 if (clnt_state == INIT_REBOOT_STATE) {
849 if (verbose) {
850 dhcpmsg(LOG_NOTICE, "Client: "
851 "%1$s is requesting "
852 "verification of %2$s "
853 "owned by %3$s\n",
854 pcd->cidbuf,
855 inet_ntop(AF_INET, &cipaddr,
856 ntoab, sizeof (ntoab)),
857 inet_ntop(AF_INET, &sipaddr,
858 ntoac, sizeof (ntoac)));
859 }
860 goto leave_ack;
861 } else {
862 /* RENEW/REBIND - wait for primary */
863 if (boot_secs < (ushort_t)renog_secs)
864 goto leave_ack;
865 }
866
867 }
868 if (claddr.s_addr != htonl(dnp->dn_cip.s_addr)) {
869 /*
870 * Client has the wrong IP address. Nak.
871 */
872 (void) snprintf(nak_mesg, sizeof (nak_mesg),
873 "Incorrect IP address.");
874 pkt_type = NAK;
875 } else {
876 if (!(dnp->dn_flags & DN_FAUTOMATIC) &&
877 (lease_t)dnp->dn_lease < (lease_t)now) {
878 (void) snprintf(nak_mesg,
879 sizeof (nak_mesg),
880 "Lease has expired.");
881 pkt_type = NAK;
882 }
883 }
884 } else {
885 if (clnt_state == RENEW_REBIND_STATE) {
886 dhcpmsg(LOG_ERR, "Client: %1$s is trying to "
887 "renew %2$s, an IP address it has not "
888 "leased.\n", pcd->cidbuf, inet_ntop(AF_INET,
889 &ciaddr, ntoab, sizeof (ntoab)));
890 goto leave_ack;
891 }
892 /*
893 * There is no such client registered for this
894 * address. Check if their address is on the correct
895 * net. If it is, then we'll assume that some other,
896 * non-database sharing DHCP server knows about this
897 * client. If the client is on the wrong net, NAK'em.
898 */
899 if ((claddr.s_addr & pnd->subnet.s_addr) ==
900 pnd->net.s_addr) {
901 /* Right net, but no record of client. */
902 if (verbose) {
903 dhcpmsg(LOG_INFO,
904 "Client: %1$s is trying to verify "
905 "unrecorded address: %2$s, "
906 "ignored.\n", pcd->cidbuf,
907 inet_ntop(AF_INET, &claddr,
908 ntoab, sizeof (ntoab)));
909 }
910 goto leave_ack;
911 } else {
912 if (ciaddr.s_addr == 0L) {
913 (void) snprintf(nak_mesg,
914 sizeof (nak_mesg),
915 "No valid configuration exists on "
916 "network: %s", pnd->network);
917 pkt_type = NAK;
918 } else {
919 if (verbose) {
920 dhcpmsg(LOG_INFO,
921 "Client: %1$s is not "
922 "recorded as having "
923 "address: %2$s\n",
924 pcd->cidbuf,
925 inet_ntop(AF_INET, &ciaddr,
926 ntoab, sizeof (ntoab)));
927 }
928 goto leave_ack;
929 }
930 }
931 }
932 }
933
934 /*
935 * Produce the appropriate response.
936 */
937 if (pkt_type == NAK) {
938 rep_pktp = gen_reply_pkt(pcd, plp, NAK, &replen, &optp,
939 &ifp->addr);
940 /*
941 * Setting yiaddr to the client's ciaddr abuses the
942 * semantics of yiaddr, So we set this to 0L.
943 *
944 * We twiddle the broadcast flag to force the
945 * server/relay agents to broadcast the NAK.
946 *
947 * Exception: If a client's lease has expired, and it
948 * is still trying to renegotiate its lease, AND ciaddr
949 * is set, AND ciaddr is on a "remote" net, unicast the
950 * NAK. Gross, huh? But SPA could make this happen with
951 * super short leases.
952 */
953 rep_pktp->yiaddr.s_addr = 0L;
954 if (ciaddr.s_addr != 0L &&
955 (ciaddr.s_addr & pnd->subnet.s_addr) != pnd->net.s_addr) {
956 dest_in.s_addr = ciaddr.s_addr;
957 } else {
958 rep_pktp->flags |= htons(BCAST_MASK);
959 dest_in.s_addr = INADDR_BROADCAST;
960 }
961
962 *optp++ = CD_MESSAGE;
963 *optp++ = (uchar_t)strlen(nak_mesg);
964 (void) memcpy(optp, nak_mesg, strlen(nak_mesg));
965 optp += strlen(nak_mesg);
966 *optp = CD_END;
967 actual_len = BASE_PKT_SIZE + (uint_t)(optp - rep_pktp->options);
968 if (actual_len < sizeof (PKT))
969 actual_len = sizeof (PKT);
970
971 (void) send_reply(ifp, rep_pktp, actual_len, &dest_in);
972
973 logtrans(P_DHCP, L_NAK, 0, dest_in, server_ip, plp);
974 } else {
975 rep_pktp = gen_reply_pkt(pcd, plp, ACK, &replen, &optp,
976 &ifp->addr);
977
978 /* Set the client's IP address */
979 rep_pktp->yiaddr.s_addr = htonl(dnp->dn_cip.s_addr);
980 dest_in.s_addr = htonl(dnp->dn_cip.s_addr);
981
982 /*
983 * Macros are evaluated this way: First apply parameters
984 * from a client class macro (if present), then apply
985 * those from the network macro (if present), then apply
986 * those from the server macro (if present), and finally
987 * apply those from a client id macro (if present).
988 */
989 ecp = vecp = NULL;
990 class_vecp = class_ecp = NULL;
991 net_vecp = net_ecp = NULL;
992 macro_vecp = macro_ecp = NULL;
993 cid_vecp = cid_ecp = NULL;
994
995 if (!no_dhcptab) {
996 open_macros();
997 if ((net_mp = get_macro(pnd->network)) != NULL)
998 net_ecp = net_mp->head;
999 if ((pkt_mp = get_macro(dnp->dn_macro)) != NULL)
1000 macro_ecp = pkt_mp->head;
1001 if ((cid_mp = get_macro(pcd->cidbuf)) != NULL)
1002 cid_ecp = cid_mp->head;
1003 if (class_id != NULL) {
1004 if ((class_mp = get_macro(class_id)) != NULL) {
1005 class_vecp = vendor_encodes(class_mp,
1006 class_id);
1007 class_ecp = class_mp->head;
1008 }
1009 if (net_mp != NULL) {
1010 net_vecp = vendor_encodes(net_mp,
1011 class_id);
1012 }
1013 if (pkt_mp != NULL)
1014 macro_vecp = vendor_encodes(pkt_mp,
1015 class_id);
1016 if (cid_mp != NULL) {
1017 cid_vecp = vendor_encodes(cid_mp,
1018 class_id);
1019 }
1020 vecp = combine_encodes(class_vecp, net_vecp,
1021 ENC_COPY);
1022 vecp = combine_encodes(vecp, macro_vecp,
1023 ENC_DONT_COPY);
1024 vecp = combine_encodes(vecp, cid_vecp,
1025 ENC_DONT_COPY);
1026 }
1027 if (class_ecp != NULL) {
1028 ecp = combine_encodes(class_ecp, net_ecp,
1029 ENC_COPY);
1030 } else
1031 ecp = dup_encode_list(net_ecp);
1032
1033 ecp = combine_encodes(ecp, macro_ecp, ENC_DONT_COPY);
1034 ecp = combine_encodes(ecp, cid_ecp, ENC_DONT_COPY);
1035
1036 ncipaddr.s_addr = htonl(dnp->dn_cip.s_addr);
1037 /*
1038 * If the server is configured to do host name updates
1039 * and the REQUEST packet contains a hostname request,
1040 * see whether we can honor it.
1041 *
1042 * First, determine (via name_avail()) whether the host
1043 * name is unassigned or belongs to an unleased IP
1044 * address under our control. If not, we won't do a
1045 * host name update on behalf of the client.
1046 *
1047 * Second, if we own the IP address and it is in the
1048 * correct network table, see whether an update is
1049 * necessary (or, in the lucky case, whether the name
1050 * requested already belongs to that address), in which
1051 * case we need do nothing more than return the option.
1052 */
1053 if ((nsutimeout_secs != DHCP_NO_NSU) &&
1054 (plp->opts[CD_HOSTNAME] != NULL)) {
1055 char hname[MAXHOSTNAMELEN + 1];
1056 int hlen;
1057 struct in_addr ia, *iap = &ia;
1058
1059 /* turn hostname option into a string */
1060 hlen = plp->opts[CD_HOSTNAME]->len;
1061 hlen = MIN(hlen, MAXHOSTNAMELEN);
1062 (void) memcpy(hname,
1063 plp->opts[CD_HOSTNAME]->value, hlen);
1064 hname[hlen] = '\0';
1065
1066 nlp = NULL;
1067 if (name_avail(hname, pcd, plp, &nlp, ecp,
1068 &iap)) {
1069 ENCODE *hecp;
1070
1071 /*
1072 * If we pass this test, it means either
1073 * no address is currently associated
1074 * with the requested host name (iap is
1075 * NULL) or the address doesn't match
1076 * the one to be leased; in either case
1077 * an update attempt is needed.
1078 *
1079 * Otherwise (in the else case), we need
1080 * only send the response - the name and
1081 * address already match.
1082 */
1083 if ((iap == NULL) || (iap->s_addr !=
1084 dnp->dn_cip.s_addr)) {
1085 if (do_nsupdate(dnp->dn_cip,
1086 ecp, plp)) {
1087 hecp = make_encode(
1088 DSYM_STANDARD,
1089 CD_HOSTNAME,
1090 strlen(hname),
1091 hname,
1092 ENC_COPY);
1093 replace_encode(&ecp,
1094 hecp,
1095 ENC_DONT_COPY);
1096 hostname_update =
1097 B_TRUE;
1098 }
1099 } else {
1100 hecp = make_encode(
1101 DSYM_STANDARD,
1102 CD_HOSTNAME,
1103 strlen(hname), hname,
1104 ENC_COPY);
1105 replace_encode(&ecp, hecp,
1106 ENC_DONT_COPY);
1107 hostname_update = B_TRUE;
1108 }
1109 if (nlp != NULL)
1110 dhcp_free_dd_list(pnd->dh, nlp);
1111 }
1112 }
1113
1114 /*
1115 * If dhcptab configured to return hostname, do so.
1116 */
1117 if ((hostname_update == B_FALSE) &&
1118 (find_encode(ecp, DSYM_INTERNAL,
1119 CD_BOOL_HOSTNAME) != NULL)) {
1120 struct hostent h, *hp;
1121 ENCODE *hecp;
1122 char hbuf[NSS_BUFLEN_HOSTS];
1123 hp = gethostbyaddr_r((char *)&ncipaddr,
1124 sizeof (struct in_addr), AF_INET, &h, hbuf,
1125 sizeof (hbuf), &err);
1126 if (hp != NULL) {
1127 hecp = make_encode(DSYM_STANDARD,
1128 CD_HOSTNAME, strlen(hp->h_name),
1129 hp->h_name, ENC_COPY);
1130 replace_encode(&ecp, hecp,
1131 ENC_DONT_COPY);
1132 }
1133 }
1134
1135 /*
1136 * If dhcptab configured to echo client class, do so.
1137 */
1138 if (plp->opts[CD_CLASS_ID] != NULL &&
1139 find_encode(ecp, DSYM_INTERNAL,
1140 CD_BOOL_ECHO_VCLASS) != NULL) {
1141 ENCODE *echo_ecp;
1142 DHCP_OPT *op = plp->opts[CD_CLASS_ID];
1143 echo_ecp = make_encode(DSYM_STANDARD,
1144 CD_CLASS_ID, op->len, op->value,
1145 ENC_COPY);
1146 replace_encode(&ecp, echo_ecp, ENC_DONT_COPY);
1147 }
1148 }
1149
1150 if (dnp->dn_flags & DN_FAUTOMATIC || dnp->dn_lease == DHCP_PERM)
1151 oldlease = DHCP_PERM;
1152 else {
1153 if (plp->opts[CD_SERVER_ID] != NULL) {
1154 /*
1155 * Offered absolute Lease time is cached
1156 * in the lease field of the record. If
1157 * that's expired, then they'll get the
1158 * policy value again here. Must have been
1159 * LONG time between DISC/REQ!
1160 */
1161 if ((lease_t)dnp->dn_lease < (lease_t)now)
1162 oldlease = (lease_t)0;
1163 else
1164 oldlease = dnp->dn_lease - now;
1165 } else
1166 oldlease = dnp->dn_lease - now;
1167 }
1168
1169 if (find_encode(ecp, DSYM_INTERNAL, CD_BOOL_LEASENEG) !=
1170 NULL)
1171 negot = B_TRUE;
1172 else
1173 negot = B_FALSE;
1174
1175 /*
1176 * Modify changed fields in new database record.
1177 */
1178 ndn = *dnp; /* struct copy */
1179 (void) memcpy(ndn.dn_cid, pcd->cid, pcd->cid_len);
1180 ndn.dn_cid_len = pcd->cid_len;
1181
1182 /*
1183 * This is a little longer than we offered (not taking into
1184 * account the secs field), but since I trust the UNIX
1185 * clock better than the PC's, it is a good idea to give
1186 * the PC a little more time than it thinks, just due to
1187 * clock slop on PC's.
1188 */
1189 newlease = config_lease(plp, &ndn, &ecp, oldlease, negot);
1190
1191 if (newlease != DHCP_PERM)
1192 ndn.dn_lease = now + newlease;
1193 else
1194 ndn.dn_lease = DHCP_PERM;
1195
1196
1197 /*
1198 * It is critical to write the database record if the
1199 * client is in the INIT state, so we don't reply to the
1200 * client if this fails. However, if the client is simply
1201 * trying to verify its address or extend its lease, then
1202 * we'll reply regardless of the status of the write,
1203 * although we'll return the old lease time.
1204 *
1205 * If the client is in the INIT_REBOOT state, and the
1206 * lease time hasn't changed, we don't bother with the
1207 * write, since nothing has changed.
1208 */
1209 if (clnt_state == INIT_STATE || oldlease != newlease) {
1210
1211 write_error = dhcp_modify_dd_entry(pnd->dh, dnp, &ndn);
1212
1213 /* Keep state of the cached entry current. */
1214 if (write_error == DSVC_SUCCESS) {
1215 *dnp = ndn; /* struct copy */
1216 }
1217 } else {
1218 if (verbose) {
1219 dhcpmsg(LOG_INFO,
1220 "Database write unnecessary for "
1221 "DHCP client: "
1222 "%1$s, %2$s\n", pcd->cidbuf,
1223 inet_ntop(AF_INET, &ncipaddr,
1224 ntoab, sizeof (ntoab)));
1225 }
1226 }
1227 if (write_error == DSVC_SUCCESS ||
1228 clnt_state == INIT_REBOOT_STATE) {
1229
1230 if (write_error != DSVC_SUCCESS)
1231 set_lease_option(&ecp, oldlease);
1232 else {
1233 /* Note that the conversation has completed. */
1234 pcd->state = ACK;
1235 }
1236
1237 if (plp->opts[CD_REQUEST_LIST])
1238 add_request_list(ifp, plp, &ecp, &ncipaddr);
1239
1240 /* Now load all the asked for / configured options */
1241 actual_len = load_options(DHCP_DHCP_CLNT |
1242 DHCP_SEND_LEASE, plp, rep_pktp, replen, optp, ecp,
1243 vecp);
1244
1245 if (actual_len < sizeof (PKT))
1246 actual_len = sizeof (PKT);
1247 if (verbose) {
1248 dhcpmsg(LOG_INFO,
1249 "Client: %1$s maps to IP: %2$s\n",
1250 pcd->cidbuf,
1251 inet_ntop(AF_INET, &ncipaddr,
1252 ntoab, sizeof (ntoab)));
1253 }
1254 (void) send_reply(ifp, rep_pktp, actual_len, &dest_in);
1255
1256 if (clnt_state == INIT_STATE)
1257 log = L_ASSIGN;
1258 else
1259 log = L_REPLY;
1260
1261 logtrans(P_DHCP, log, ndn.dn_lease, ncipaddr,
1262 server_ip, plp);
1263 }
1264
1265 free_encode_list(ecp);
1266 free_encode_list(vecp);
1267 if (!no_dhcptab)
1268 close_macros();
1269 }
1270
1271 leave_ack:
1272 if (rep_pktp != NULL)
1273 free(rep_pktp);
1274 if (dncp != NULL)
1275 dhcp_free_dd_list(pnd->dh, dncp);
1276 if (dnlp != NULL && !existing_offer)
1277 dhcp_free_dd_list(pnd->dh, dnlp);
1278 }
1279
1280 /* Reacting to a client's DECLINE or RELEASE. */
1281 static void
1282 dhcp_dec_rel(dsvc_clnt_t *pcd, PKT_LIST *plp, int type)
1283 {
1284 char *fmtp;
1285 dn_rec_t *dnp, dn, ndn;
1286 dsvc_dnet_t *pnd;
1287 struct in_addr ip;
1288 int err = 0;
1289 DHCP_MSG_CATEGORIES log;
1290 dn_rec_list_t *dncp, *dnlp = NULL;
1291 uint32_t query;
1292 char ipb[INET_ADDRSTRLEN];
1293 char clnt_msg[DHCP_MAX_OPT_SIZE];
1294
1295 pnd = pcd->pnd;
1296
1297 if (type == DECLINE) {
1298 if (plp->opts[CD_REQUESTED_IP_ADDR] &&
1299 plp->opts[CD_REQUESTED_IP_ADDR]->len ==
1300 sizeof (struct in_addr)) {
1301 (void) memcpy((char *)&ip,
1302 plp->opts[CD_REQUESTED_IP_ADDR]->value,
1303 sizeof (struct in_addr));
1304 }
1305 } else
1306 ip.s_addr = plp->pkt->ciaddr.s_addr;
1307
1308 (void) inet_ntop(AF_INET, &ip, ipb, sizeof (ipb));
1309
1310 /* Look for a matching IP address and Client ID */
1311
1312 DSVC_QINIT(query);
1313 DSVC_QEQ(query, DN_QCID|DN_QCIP);
1314 (void) memcpy(dn.dn_cid, pcd->cid, pcd->cid_len);
1315 dn.dn_cid_len = pcd->cid_len;
1316 dn.dn_cip.s_addr = ntohl(ip.s_addr);
1317
1318 dncp = NULL;
1319 dnlp = dhcp_lookup_dd_classify(pcd->pnd, B_FALSE, query, -1,
1320 &dn, (void **)&dncp, S_CID);
1321 assert(dncp == NULL);
1322
1323 if (dnlp == NULL) {
1324 if (verbose) {
1325 if (type == DECLINE) {
1326 fmtp = "Unregistered client: %1$s is "
1327 "DECLINEing address: %2$s.\n";
1328 } else {
1329 fmtp = "Unregistered client: %1$s is "
1330 "RELEASEing address: %2$s.\n";
1331 }
1332 dhcpmsg(LOG_INFO, fmtp, pcd->cidbuf, ipb);
1333 }
1334 return;
1335 }
1336
1337 dnp = dnlp->dnl_rec;
1338 ndn = *dnp; /* struct copy */
1339
1340 /* If the entry is not one of ours, then give up. */
1341 if (match_ownerip(htonl(ndn.dn_sip.s_addr)) == NULL) {
1342 if (verbose) {
1343 if (type == DECLINE) {
1344 fmtp = "Client: %1$s is DECLINEing: "
1345 "%2$s not owned by this server.\n";
1346 } else {
1347 fmtp = "Client: %1$s is RELEASEing: "
1348 "%2$s not owned by this server.\n";
1349 }
1350 dhcpmsg(LOG_INFO, fmtp, pcd->cidbuf, ipb);
1351 }
1352 goto leave_dec_rel;
1353 }
1354
1355 if (type == DECLINE) {
1356 log = L_DECLINE;
1357 dhcpmsg(LOG_ERR, "Client: %1$s DECLINED address: %2$s.\n",
1358 pcd->cidbuf, ipb);
1359 if (plp->opts[CD_MESSAGE]) {
1360 dhcpmsg(LOG_ERR, "DECLINE: client message: %s\n",
1361 disp_clnt_msg(plp, clnt_msg, sizeof (clnt_msg)));
1362 }
1363 ndn.dn_flags |= DN_FUNUSABLE;
1364 } else {
1365 log = L_RELEASE;
1366 if (ndn.dn_flags & DN_FMANUAL) {
1367 dhcpmsg(LOG_ERR,
1368 "Client: %1$s is trying to RELEASE manual "
1369 "address: %2$s\n", pcd->cidbuf, ipb);
1370 goto leave_dec_rel;
1371 }
1372 if (verbose) {
1373 dhcpmsg(LOG_INFO,
1374 "Client: %1$s RELEASED address: %2$s\n",
1375 pcd->cidbuf, ipb);
1376 if (plp->opts[CD_MESSAGE]) {
1377 dhcpmsg(LOG_INFO,
1378 "RELEASE: client message: %s\n",
1379 disp_clnt_msg(plp, clnt_msg,
1380 sizeof (clnt_msg)));
1381 }
1382 }
1383 }
1384
1385 /* Clear out the cid and lease fields */
1386 if (!(ndn.dn_flags & DN_FMANUAL)) {
1387 ndn.dn_cid[0] = '\0';
1388 ndn.dn_cid_len = 1;
1389 ndn.dn_lease = (lease_t)0;
1390 }
1391
1392 /* Ignore write errors. */
1393 err = dhcp_modify_dd_entry(pnd->dh, dnp, &ndn);
1394 if (err != DSVC_SUCCESS) {
1395 dhcpmsg(LOG_NOTICE,
1396 "%1$s: ERROR modifying database: %2$s for client %3$s\n",
1397 log == L_RELEASE ? "RELEASE" : "DECLINE",
1398 dhcpsvc_errmsg(err), ipb);
1399 } else {
1400 if (type == RELEASE) {
1401 /*
1402 * performance: save select_offer() lots of work by
1403 * caching this perfectly good ip address in freerec.
1404 */
1405 *(dnlp->dnl_rec) = ndn; /* struct copy */
1406 add_dnet_cache(pnd, dnlp);
1407 dnlp = NULL;
1408 }
1409 }
1410
1411 logtrans(P_DHCP, log, ndn.dn_lease, ip, server_ip, plp);
1412
1413 leave_dec_rel:
1414
1415 if (dnlp != NULL)
1416 dhcp_free_dd_list(pnd->dh, dnlp);
1417 }
1418
1419 /*
1420 * Responding to an INFORM message.
1421 *
1422 * INFORM messages are received from clients that already have their network
1423 * parameters (such as IP address and subnet mask), but wish to receive
1424 * other configuration parameters. The server will not check for an existing
1425 * lease as clients may have obtained their network parameters by some
1426 * means other than DHCP. Similarly, the DHCPACK generated in response to
1427 * the INFORM message will not include lease time information. All other
1428 * configuration parameters are returned.
1429 */
1430 static void
1431 dhcp_inform(dsvc_clnt_t *pcd, PKT_LIST *plp)
1432 {
1433 uint_t replen;
1434 int used_pkt_len;
1435 PKT *rep_pktp = NULL;
1436 uchar_t *optp;
1437 ENCODE *ecp, *vecp, *class_ecp, *class_vecp,
1438 *cid_ecp, *cid_vecp, *net_ecp, *net_vecp;
1439 MACRO *net_mp, *class_mp, *cid_mp;
1440 dsvc_dnet_t *pnd;
1441 char *class_id;
1442 char class_idbuf[DSYM_CLASS_SIZE];
1443 IF *ifp = pcd->ifp;
1444
1445 pnd = pcd->pnd;
1446 class_id = get_class_id(plp, class_idbuf, sizeof (class_idbuf));
1447
1448 /*
1449 * Macros are evaluated this way: First apply parameters from
1450 * a client class macro (if present), then apply those from the
1451 * network macro (if present), and finally apply those from a
1452 * client id macro (if present).
1453 */
1454 ecp = vecp = NULL;
1455 net_vecp = net_ecp = NULL;
1456 class_vecp = class_ecp = NULL;
1457 cid_vecp = cid_ecp = NULL;
1458
1459 if (!no_dhcptab) {
1460 open_macros();
1461 if ((net_mp = get_macro(pnd->network)) != NULL)
1462 net_ecp = net_mp->head;
1463 if ((cid_mp = get_macro(pcd->cidbuf)) != NULL)
1464 cid_ecp = cid_mp->head;
1465 if (class_id != NULL) {
1466 if ((class_mp = get_macro(class_id)) != NULL) {
1467 class_vecp = vendor_encodes(class_mp,
1468 class_id);
1469 class_ecp = class_mp->head;
1470 }
1471 if (net_mp != NULL)
1472 net_vecp = vendor_encodes(net_mp, class_id);
1473 if (cid_mp != NULL)
1474 cid_vecp = vendor_encodes(cid_mp, class_id);
1475 vecp = combine_encodes(class_vecp, net_vecp,
1476 ENC_COPY);
1477 vecp = combine_encodes(vecp, cid_vecp, ENC_DONT_COPY);
1478 }
1479
1480 ecp = combine_encodes(class_ecp, net_ecp, ENC_COPY);
1481 ecp = combine_encodes(ecp, cid_ecp, ENC_DONT_COPY);
1482 }
1483
1484 /* First get a generic reply packet. */
1485 rep_pktp = gen_reply_pkt(pcd, plp, ACK, &replen, &optp, &ifp->addr);
1486
1487 /*
1488 * Client is requesting specific options. let's try and ensure it
1489 * gets what it wants, if at all possible.
1490 */
1491 if (plp->opts[CD_REQUEST_LIST] != NULL)
1492 add_request_list(ifp, plp, &ecp, &plp->pkt->ciaddr);
1493
1494 /*
1495 * Explicitly set the ciaddr to be that which the client gave
1496 * us.
1497 */
1498 rep_pktp->ciaddr.s_addr = plp->pkt->ciaddr.s_addr;
1499
1500 /*
1501 * Now load all the asked for / configured options. DON'T send
1502 * any lease time info!
1503 */
1504 used_pkt_len = load_options(DHCP_DHCP_CLNT, plp, rep_pktp, replen, optp,
1505 ecp, vecp);
1506
1507 free_encode_list(ecp);
1508 free_encode_list(vecp);
1509 if (!no_dhcptab)
1510 close_macros();
1511
1512 if (used_pkt_len < sizeof (PKT))
1513 used_pkt_len = sizeof (PKT);
1514
1515 (void) send_reply(ifp, rep_pktp, used_pkt_len, &plp->pkt->ciaddr);
1516
1517 logtrans(P_DHCP, L_INFORM, 0, plp->pkt->ciaddr, server_ip, plp);
1518
1519 leave_inform:
1520 if (rep_pktp != NULL)
1521 free(rep_pktp);
1522 }
1523
1524 static char *
1525 disp_clnt_msg(PKT_LIST *plp, char *bufp, int len)
1526 {
1527 uchar_t tlen;
1528
1529 bufp[0] = '\0'; /* null string */
1530
1531 if (plp && plp->opts[CD_MESSAGE]) {
1532 tlen = ((uchar_t)len < plp->opts[CD_MESSAGE]->len) ?
1533 (len - 1) : plp->opts[CD_MESSAGE]->len;
1534 (void) memcpy(bufp, plp->opts[CD_MESSAGE]->value, tlen);
1535 bufp[tlen] = '\0';
1536 }
1537 return (bufp);
1538 }
1539
1540 /*
1541 * serverip expected in host order
1542 */
1543 static PKT *
1544 gen_reply_pkt(dsvc_clnt_t *pcd, PKT_LIST *plp, int type, uint_t *len,
1545 uchar_t **optpp, struct in_addr *serverip)
1546 {
1547 PKT *reply_pktp;
1548 uint16_t plen;
1549
1550 /*
1551 * We need to determine the packet size. Perhaps the client has told
1552 * us?
1553 */
1554 if (plp->opts[CD_MAX_DHCP_SIZE]) {
1555 if (plp->opts[CD_MAX_DHCP_SIZE]->len != sizeof (uint16_t)) {
1556 dhcpmsg(LOG_ERR, "Garbled MAX DHCP message size option "
1557 "from\nclient: '%1$s'. Len is %2$d, when it should "
1558 "be %3$d. Defaulting to %4$d.\n",
1559 pcd->cidbuf,
1560 plp->opts[CD_MAX_DHCP_SIZE]->len,
1561 sizeof (uint16_t), DHCP_DEF_MAX_SIZE);
1562 plen = DHCP_DEF_MAX_SIZE;
1563 } else {
1564 (void) memcpy(&plen, plp->opts[CD_MAX_DHCP_SIZE]->value,
1565 sizeof (uint16_t));
1566 plen = ntohs(plen);
1567 if (plen < DHCP_DEF_MAX_SIZE)
1568 plen = DHCP_DEF_MAX_SIZE;
1569 }
1570 } else {
1571 /*
1572 * Define size to be a fixed length. Too hard to add up all
1573 * possible class id, macro, and hostname/lease time options
1574 * without doing just about as much work as constructing the
1575 * whole reply packet.
1576 */
1577 plen = DHCP_MAX_REPLY_SIZE;
1578 }
1579
1580 /* Generate a generically initialized BOOTP packet */
1581 reply_pktp = gen_bootp_pkt(plen, plp->pkt);
1582
1583 reply_pktp->op = BOOTREPLY;
1584 *optpp = reply_pktp->options;
1585
1586 /*
1587 * Set pkt type.
1588 */
1589 *(*optpp)++ = (uchar_t)CD_DHCP_TYPE;
1590 *(*optpp)++ = (uchar_t)1;
1591 *(*optpp)++ = (uchar_t)type;
1592
1593 /*
1594 * All reply packets have server id set.
1595 */
1596 *(*optpp)++ = (uchar_t)CD_SERVER_ID;
1597 *(*optpp)++ = (uchar_t)4;
1598 #if defined(_LITTLE_ENDIAN)
1599 *(*optpp)++ = (uchar_t)(serverip->s_addr & 0xff);
1600 *(*optpp)++ = (uchar_t)((serverip->s_addr >> 8) & 0xff);
1601 *(*optpp)++ = (uchar_t)((serverip->s_addr >> 16) & 0xff);
1602 *(*optpp)++ = (uchar_t)((serverip->s_addr >> 24) & 0xff);
1603 #else
1604 *(*optpp)++ = (uchar_t)((serverip->s_addr >> 24) & 0xff);
1605 *(*optpp)++ = (uchar_t)((serverip->s_addr >> 16) & 0xff);
1606 *(*optpp)++ = (uchar_t)((serverip->s_addr >> 8) & 0xff);
1607 *(*optpp)++ = (uchar_t)(serverip->s_addr & 0xff);
1608 #endif /* _LITTLE_ENDIAN */
1609
1610 *len = plen;
1611 return (reply_pktp);
1612 }
1613
1614 /*
1615 * If the client requests it, and either it isn't currently configured
1616 * or hasn't already been added, provide the option now. Will also work
1617 * for NULL ENCODE lists, but initializing them to point to the requested
1618 * options.
1619 *
1620 * If nsswitch contains host name services which hang, big problems occur
1621 * with dhcp server, since the main thread hangs waiting for that name
1622 * service's timeout.
1623 *
1624 * NOTE: this function should be called only after all other parameter
1625 * merges have taken place (combine_encode).
1626 */
1627 static void
1628 add_request_list(IF *ifp, PKT_LIST *plp, ENCODE **ecp, struct in_addr *ip)
1629 {
1630 ENCODE *ep, *ifecp, *end_ecp = NULL;
1631 struct hostent h, *hp;
1632 char hbuf[NSS_BUFLEN_HOSTS];
1633 int herrno;
1634
1635 /* Find the end. */
1636 if (*ecp) {
1637 for (ep = *ecp; ep->next; ep = ep->next)
1638 /* null */;
1639 end_ecp = ep;
1640 }
1641
1642 /* HOSTNAME */
1643 if (is_option_requested(plp, CD_HOSTNAME) &&
1644 (find_encode(*ecp, DSYM_STANDARD, CD_HOSTNAME) == NULL) &&
1645 (find_encode(*ecp, DSYM_INTERNAL, CD_BOOL_HOSTNAME) == NULL)) {
1646 hp = gethostbyaddr_r((char *)ip, sizeof (struct in_addr),
1647 AF_INET, &h, hbuf, sizeof (hbuf), &herrno);
1648 if (hp != NULL) {
1649 if (end_ecp) {
1650 end_ecp->next = make_encode(DSYM_STANDARD,
1651 CD_HOSTNAME, strlen(hp->h_name),
1652 hp->h_name, ENC_COPY);
1653 end_ecp = end_ecp->next;
1654 } else {
1655 end_ecp = make_encode(DSYM_STANDARD,
1656 CD_HOSTNAME, strlen(hp->h_name),
1657 hp->h_name, ENC_COPY);
1658 }
1659 }
1660 }
1661
1662 /*
1663 * all bets off for the following if thru a relay agent.
1664 */
1665 if (plp->pkt->giaddr.s_addr != 0L)
1666 return;
1667
1668 /* SUBNET MASK */
1669 if (is_option_requested(plp, CD_SUBNETMASK) && find_encode(*ecp,
1670 DSYM_STANDARD, CD_SUBNETMASK) == NULL) {
1671 ifecp = find_encode(ifp->ecp, DSYM_STANDARD, CD_SUBNETMASK);
1672 if (end_ecp) {
1673 end_ecp->next = dup_encode(ifecp);
1674 end_ecp = end_ecp->next;
1675 } else
1676 end_ecp = dup_encode(ifecp);
1677 }
1678
1679 /* BROADCAST ADDRESS */
1680 if (is_option_requested(plp, CD_BROADCASTADDR) && find_encode(*ecp,
1681 DSYM_STANDARD, CD_BROADCASTADDR) == NULL) {
1682 ifecp = find_encode(ifp->ecp, DSYM_STANDARD,
1683 CD_BROADCASTADDR);
1684 if (end_ecp) {
1685 end_ecp->next = dup_encode(ifecp);
1686 end_ecp = end_ecp->next;
1687 } else
1688 end_ecp = dup_encode(ifecp);
1689 }
1690
1691 /* IP MTU */
1692 if (is_option_requested(plp, CD_MTU) && find_encode(*ecp,
1693 DSYM_STANDARD, CD_MTU) == NULL) {
1694 ifecp = find_encode(ifp->ecp, DSYM_STANDARD, CD_MTU);
1695 if (end_ecp) {
1696 end_ecp->next = dup_encode(ifecp);
1697 end_ecp = end_ecp->next;
1698 } else
1699 end_ecp = dup_encode(ifecp);
1700 }
1701
1702 if (*ecp == NULL)
1703 *ecp = end_ecp;
1704 }
1705
1706 /*
1707 * Is a specific option requested? Returns True if so, False otherwise.
1708 */
1709 static int
1710 is_option_requested(PKT_LIST *plp, ushort_t code)
1711 {
1712 uchar_t c, *tp;
1713 DHCP_OPT *cp = plp->opts[CD_REQUEST_LIST];
1714
1715 for (c = 0, tp = (uchar_t *)cp->value; c < cp->len; c++, tp++) {
1716 if (*tp == (uchar_t)code)
1717 return (B_TRUE);
1718 }
1719 return (B_FALSE);
1720 }
1721
1722 /*
1723 * Locates lease option, if possible, otherwise allocates an encode and
1724 * appends it to the end. Changes current lease setting.
1725 *
1726 * TODO: ugh. We don't address the case where the Lease time changes, but
1727 * T1 and T2 don't. We don't want T1 or T2 to be greater than the lease
1728 * time! Perhaps T1 and T2 should be a percentage of lease time... Later..
1729 */
1730 static void
1731 set_lease_option(ENCODE **ecpp, lease_t lease)
1732 {
1733 ENCODE *ep, *prev_ep, *lease_ep;
1734
1735 lease = htonl(lease);
1736
1737 if (ecpp != NULL && (lease_ep = find_encode(*ecpp, DSYM_STANDARD,
1738 CD_LEASE_TIME)) != NULL && lease_ep->len == sizeof (lease_t)) {
1739 (void) memcpy(lease_ep->data, (void *)&lease, sizeof (lease_t));
1740 } else {
1741 if (*ecpp != NULL) {
1742 for (prev_ep = ep = *ecpp; ep != NULL; ep = ep->next)
1743 prev_ep = ep;
1744 prev_ep->next = make_encode(DSYM_STANDARD,
1745 CD_LEASE_TIME, sizeof (lease_t), &lease, ENC_COPY);
1746 } else {
1747 *ecpp = make_encode(DSYM_STANDARD, CD_LEASE_TIME,
1748 sizeof (lease_t), &lease, ENC_COPY);
1749 (*ecpp)->next = NULL;
1750 }
1751 }
1752 }
1753 /*
1754 * Sets appropriate option in passed ENCODE list for lease. Returns
1755 * calculated relative lease time.
1756 */
1757 static int
1758 config_lease(PKT_LIST *plp, dn_rec_t *dnp, ENCODE **ecpp, lease_t oldlease,
1759 boolean_t negot)
1760 {
1761 lease_t newlease, rel_current;
1762 ENCODE *lease_ecp;
1763
1764 if (ecpp != NULL && (lease_ecp = find_encode(*ecpp, DSYM_STANDARD,
1765 CD_LEASE_TIME)) != NULL && lease_ecp->len == sizeof (lease_t)) {
1766 (void) memcpy((void *)&rel_current, lease_ecp->data,
1767 sizeof (lease_t));
1768 rel_current = htonl(rel_current);
1769 } else
1770 rel_current = (lease_t)DEFAULT_LEASE;
1771
1772 if (dnp->dn_flags & DN_FAUTOMATIC || !negot) {
1773 if (dnp->dn_flags & DN_FAUTOMATIC)
1774 newlease = ntohl(DHCP_PERM);
1775 else {
1776 /* sorry! */
1777 if (oldlease)
1778 newlease = oldlease;
1779 else
1780 newlease = rel_current;
1781 }
1782 } else {
1783 /*
1784 * lease is not automatic and is negotiable!
1785 * If the dhcp-network lease is bigger than the current
1786 * policy value, then let the client benefit from this
1787 * situation.
1788 */
1789 if (oldlease > rel_current)
1790 rel_current = oldlease;
1791
1792 if (plp->opts[CD_LEASE_TIME] &&
1793 plp->opts[CD_LEASE_TIME]->len == sizeof (lease_t)) {
1794 /*
1795 * Client is requesting a lease renegotiation.
1796 */
1797 (void) memcpy((void *)&newlease,
1798 plp->opts[CD_LEASE_TIME]->value, sizeof (lease_t));
1799
1800 newlease = ntohl(newlease);
1801
1802 /*
1803 * Note that this comparison handles permanent
1804 * leases as well. Limit lease to configured value.
1805 */
1806 if (newlease > rel_current)
1807 newlease = rel_current;
1808 } else
1809 newlease = rel_current;
1810 }
1811
1812 set_lease_option(ecpp, newlease);
1813
1814 return (newlease);
1815 }
1816
1817 /*
1818 * If a packet has the classid set, return the value, else return null.
1819 */
1820 char *
1821 get_class_id(PKT_LIST *plp, char *bufp, int len)
1822 {
1823 uchar_t *ucp, ulen;
1824 char *retp;
1825
1826 if (plp->opts[CD_CLASS_ID]) {
1827 /*
1828 * If the class id is set, see if there is a macro by this
1829 * name. If so, then "OR" the ENCODE settings of the class
1830 * macro with the packet macro. Settings in the packet macro
1831 * OVERRIDE settings in the class macro.
1832 */
1833 ucp = plp->opts[CD_CLASS_ID]->value;
1834 ulen = plp->opts[CD_CLASS_ID]->len;
1835 if (len < ulen)
1836 ulen = len;
1837 (void) memcpy(bufp, ucp, ulen);
1838 bufp[ulen] = '\0';
1839
1840 retp = bufp;
1841 } else
1842 retp = NULL;
1843
1844 return (retp);
1845 }
1846
1847 /*
1848 * Checks whether an offer ip address in the per net inet address
1849 * cache.
1850 *
1851 * pnd - per net structure
1852 * reservep - address to check, in network order.
1853 */
1854 static boolean_t
1855 check_offer(dsvc_dnet_t *pnd, struct in_addr *reservep)
1856 {
1857 dsvc_clnt_t tpcd;
1858
1859 tpcd.off_ip.s_addr = reservep->s_addr;
1860
1861 return (hash_Lookup(pnd->itable, reservep, sizeof (struct in_addr),
1862 clnt_netcmp, &tpcd, B_FALSE) == NULL ? B_TRUE : B_FALSE);
1863 }
1864
1865 /*
1866 * Adds or updates an offer to the per client data structure. The client
1867 * struct is hashed by clientid into the per net ctable hash table, and
1868 * by offer address in the itable hash table, which is used to reserve the
1869 * ip address. Lease time is expected to be set by caller.
1870 * Will update existing OFFER if already provided.
1871 *
1872 * This implementation does not consider the fact that an offer can be
1873 * sent out via more than one interface, so dsvc_clnt_t.ifp should
1874 * really be a list or the itable's entries should be lists of
1875 * dsvc_clnt_ts. As long as we don't change this, we assume that the
1876 * client will eventually REQUEST the last offer we have sent out
1877 * because when we receive the same DISCOVER via multiple interfaces,
1878 * we always update the same offer cache entry so its ifp is always
1879 * the interface we received the last DISCOVER on.
1880 *
1881 * pcd - per client data struct.
1882 * dnlp - pointer to pointer to current container entry. Performance: caching
1883 * reduces datastore activity, structure copying.
1884 * nlease - new lease time.
1885 * reservep - new offer address (expected in network order).
1886 * purge_cache - Multithreading: avoid redundant cache purging in
1887 * select_offer().
1888 */
1889 boolean_t
1890 update_offer(dsvc_clnt_t *pcd, dn_rec_list_t **dnlp, lease_t nlease,
1891 struct in_addr *reservep, boolean_t purge_cache)
1892 {
1893 char ntoab[INET_ADDRSTRLEN];
1894 boolean_t insert = B_TRUE;
1895 boolean_t update = B_FALSE;
1896 boolean_t offer = B_FALSE;
1897 dsvc_dnet_t *pnd = pcd->pnd;
1898 IF *ifp = pcd->ifp;
1899 dn_rec_t *dnp = NULL;
1900 struct in_addr off_ip;
1901
1902 /* Save the original datastore record. */
1903 if (dnlp != NULL && *dnlp != NULL) {
1904 if (pcd->dnlp != NULL && pcd->dnlp != *dnlp)
1905 dhcp_free_dd_list(pnd->dh, pcd->dnlp);
1906 pcd->dnlp = *dnlp;
1907 }
1908 if (pcd->dnlp != NULL)
1909 dnp = pcd->dnlp->dnl_rec;
1910
1911 /* Determine the offer address. */
1912 if (reservep == NULL && dnp != NULL)
1913 off_ip.s_addr = htonl(dnp->dn_cip.s_addr);
1914 else if (reservep != NULL)
1915 off_ip.s_addr = reservep->s_addr;
1916 else {
1917 dhcpmsg(LOG_DEBUG,
1918 "Neither offer IP nor IP to reserve present\n");
1919 assert(B_FALSE);
1920 return (B_FALSE);
1921 }
1922
1923 /* If updating, release the old offer address. */
1924 if (pcd->off_ip.s_addr == htonl(INADDR_ANY)) {
1925 offer = B_TRUE;
1926 } else {
1927 update = B_TRUE;
1928 if (pcd->off_ip.s_addr != off_ip.s_addr) {
1929 purge_offer(pcd, B_FALSE, purge_cache);
1930 offer = B_TRUE;
1931 } else
1932 insert = B_FALSE;
1933 }
1934
1935 if (nlease != 0)
1936 pcd->lease = nlease;
1937
1938 /* Prepare to insert pcd into the offer hash table. */
1939 pcd->mtime = reinit_time;
1940
1941 pcd->off_ip.s_addr = off_ip.s_addr;
1942
1943 assert(pcd->off_ip.s_addr != htonl(INADDR_ANY));
1944
1945 if (insert) {
1946 if ((pcd->ihand = hash_Insert(pnd->itable, &pcd->off_ip,
1947 sizeof (struct in_addr), clnt_netcmp, pcd, pcd)) == NULL) {
1948 if (reservep == NULL) {
1949 dhcpmsg(LOG_WARNING, "Duplicate offer of %1$s "
1950 "to client: %2$s\n",
1951 inet_ntop(AF_INET, &pcd->off_ip, ntoab,
1952 sizeof (ntoab)), pcd->cidbuf);
1953 }
1954 pcd->off_ip.s_addr = htonl(INADDR_ANY);
1955 dhcp_free_dd_list(pnd->dh, pcd->dnlp);
1956 if (dnlp != NULL && *dnlp != NULL &&
1957 pcd->dnlp == *dnlp) {
1958 *dnlp = NULL;
1959 }
1960 pcd->dnlp = NULL;
1961 return (B_FALSE);
1962 }
1963 } else
1964 hash_Dtime(pcd->ihand, time(NULL) + off_secs);
1965
1966 if (offer) {
1967 (void) mutex_lock(&ifp->ifp_mtx);
1968 ifp->offers++;
1969 (void) mutex_unlock(&ifp->ifp_mtx);
1970 }
1971
1972 if (debug) {
1973 if (reservep != NULL) {
1974 dhcpmsg(LOG_INFO, "Reserved offer: %s\n",
1975 inet_ntop(AF_INET, &pcd->off_ip,
1976 ntoab, sizeof (ntoab)));
1977 } else if (update) {
1978 dhcpmsg(LOG_INFO, "Updated offer: %s\n",
1979 inet_ntop(AF_INET, &pcd->off_ip,
1980 ntoab, sizeof (ntoab)));
1981 } else {
1982 dhcpmsg(LOG_INFO, "Added offer: %s\n",
1983 inet_ntop(AF_INET, &pcd->off_ip,
1984 ntoab, sizeof (ntoab)));
1985 }
1986 }
1987 return (B_TRUE);
1988 }
1989
1990 /*
1991 * Deletes an offer.
1992 *
1993 * pcd - per client struct
1994 * expired - has offer expired, or been purged
1995 * purge_cache - Multi-threading: avoid redundant cache purging in
1996 * select_offer().
1997 */
1998 void
1999 purge_offer(dsvc_clnt_t *pcd, boolean_t expired, boolean_t purge_cache)
2000 {
2001 char ntoab[INET_ADDRSTRLEN];
2002 dsvc_dnet_t *pnd = pcd->pnd;
2003 IF *ifp = pcd->ifp;
2004
2005 if (pcd->off_ip.s_addr != htonl(INADDR_ANY)) {
2006 if (debug) {
2007 if (expired == B_TRUE)
2008 dhcpmsg(LOG_INFO, "Freeing offer: %s\n",
2009 inet_ntop(AF_INET, &pcd->off_ip,
2010 ntoab, sizeof (ntoab)));
2011 else
2012 dhcpmsg(LOG_INFO, "Purging offer: %s\n",
2013 inet_ntop(AF_INET, &pcd->off_ip,
2014 ntoab, sizeof (ntoab)));
2015 }
2016
2017 /*
2018 * The offer cache ensures that recently granted offer
2019 * addresses won't attempt to be reused from the dnet
2020 * caches. When purging one of these offers, be sure to
2021 * remove the associated record from the dnet cache,
2022 * to avoid collisions.
2023 */
2024 if (pcd->state == ACK && pcd->dnlp != NULL) {
2025 if (purge_cache)
2026 purge_dnet_cache(pnd, pcd->dnlp->dnl_rec);
2027 dhcp_free_dd_list(pnd->dh, pcd->dnlp);
2028 pcd->dnlp = NULL;
2029 }
2030
2031
2032 /* Prepare to delete pcd from the offer hash table. */
2033 (void) hash_Delete(pnd->itable, &pcd->off_ip,
2034 sizeof (struct in_addr), clnt_netcmp, pcd, NULL);
2035
2036 pcd->off_ip.s_addr = htonl(INADDR_ANY);
2037
2038 (void) mutex_lock(&ifp->ifp_mtx);
2039 if (ifp->offers > 0)
2040 ifp->offers--;
2041 if (expired)
2042 ifp->expired++;
2043 (void) mutex_unlock(&ifp->ifp_mtx);
2044 }
2045 }
2046
2047 /*
2048 * Allocate a new entry in the dhcp-network db for the cid, taking into
2049 * account requested IP address. Verify address.
2050 *
2051 * The network portion of the address doesn't have to be the same as ours,
2052 * just owned by us. We also make sure we don't select a record which is
2053 * currently in use, by reserving the address in the offer cache. Database
2054 * records are cached up to the D_OFFER lifetime to improve performance.
2055 *
2056 * Returns: 1 if there's a usable entry for the client, 0
2057 * if not. Places the record in the dn_rec_list_t structure
2058 * pointer handed in.
2059 */
2060 /*ARGSUSED*/
2061 boolean_t
2062 select_offer(dsvc_dnet_t *pnd, PKT_LIST *plp, dsvc_clnt_t *pcd,
2063 dn_rec_list_t **dnlpp)
2064 {
2065 struct in_addr req_ip, *req_ipp = &req_ip, tip;
2066 boolean_t found = B_FALSE;
2067 time_t now;
2068 dn_rec_t dn, *dnp;
2069 dn_rec_list_t *dncp, *dnsp, *tlp;
2070 int nrecords;
2071 uint32_t query;
2072 int retry;
2073 boolean_t io_done, is_bootp;
2074 struct in_addr *oip;
2075
2076 if (plp->opts[CD_DHCP_TYPE] == NULL)
2077 is_bootp = B_TRUE;
2078 else
2079 is_bootp = B_FALSE;
2080
2081 *dnlpp = NULL;
2082 if (!is_bootp) {
2083 /*
2084 * Is the DHCP client requesting a specific address? Is so, and
2085 * we can satisfy him, do so.
2086 */
2087 if (plp->opts[CD_REQUESTED_IP_ADDR] != NULL) {
2088 (void) memcpy((void *)&req_ip,
2089 plp->opts[CD_REQUESTED_IP_ADDR]->value,
2090 sizeof (struct in_addr));
2091
2092 if ((req_ip.s_addr & pnd->subnet.s_addr) ==
2093 pnd->net.s_addr)
2094 found = B_TRUE;
2095
2096 } else if (plp->opts[CD_HOSTNAME] != NULL) {
2097 char hname[MAXHOSTNAMELEN + 1];
2098 int hlen;
2099
2100 /* turn hostname option into a string */
2101 hlen = plp->opts[CD_HOSTNAME]->len;
2102 hlen = MIN(hlen, MAXHOSTNAMELEN);
2103 (void) memcpy(hname, plp->opts[CD_HOSTNAME]->value,
2104 hlen);
2105 hname[hlen] = '\0';
2106
2107 dhcpmsg(LOG_DEBUG,
2108 "select_offer: hostname request for %s\n", hname);
2109 if (name_avail(hname, pcd, plp, dnlpp, NULL,
2110 &req_ipp) && req_ipp) {
2111 if ((req_ip.s_addr & pnd->subnet.s_addr) ==
2112 pnd->net.s_addr) {
2113 found = B_TRUE;
2114 } else if (*dnlpp != NULL) {
2115 dhcp_free_dd_list(pnd->dh, *dnlpp);
2116 *dnlpp = NULL;
2117 }
2118 dhcpmsg(LOG_DEBUG, "select_offer: hostname %s "
2119 "available, req_ip %x\n", hname,
2120 ntohl(req_ip.s_addr));
2121 } else
2122 dhcpmsg(LOG_DEBUG, "select_offer: name_avail "
2123 "false or no address for %s\n", hname);
2124 }
2125 }
2126
2127 /*
2128 * Check the offer list and table entry.
2129 */
2130 if (found && *dnlpp == NULL)
2131 found = addr_avail(pnd, pcd, dnlpp, req_ip, B_FALSE);
2132
2133 if (!found) {
2134 /*
2135 * Try to find a free entry. Look for an AVAILABLE entry
2136 * (cid == 0x00, len == 1), owned by us.
2137 * The outer loop runs through the server ips owned by us.
2138 *
2139 * Multi-threading: to improve performance, the following
2140 * algorithm coordinates accesses to the underlying table,
2141 * so only one thread is initiating lookups per network.
2142 * This is crucial, as lookup operations are expensive,
2143 * and not sufficiently malleable to allow partitioned
2144 * lookups (e.g. all that can be asked for are n free or
2145 * server-owned entries, multiple threads will retrieve
2146 * the same records).
2147 *
2148 * The three iterations through the inner loop attempt to use
2149 *
2150 * 1) the next cached entry
2151 * 2) all cached entries
2152 * 3) all free or per-server entries in the underlying table
2153 *
2154 * Since many threads are consuming the cached entries,
2155 * any thread may find itself in the role of having to
2156 * refresh the cache. We always read at least enough
2157 * entries to satisfy all current threads. Reading all
2158 * records is prohibitively expensive, and should only
2159 * be done as a last resort.
2160 *
2161 * As always, to better distribute garbage
2162 * collection and data structure aging tasks, each
2163 * thread must actively implement policy, checking
2164 * for offer expiration (which invalidates the cache).
2165 */
2166
2167 for (oip = owner_ip; oip->s_addr != INADDR_ANY; oip++) {
2168 /*
2169 * Initialize query.
2170 */
2171 DSVC_QINIT(query);
2172 DSVC_QEQ(query, DN_QCID|DN_QSIP);
2173 dn.dn_cid[0] = '\0';
2174 dn.dn_cid_len = 1;
2175 dn.dn_sip.s_addr = ntohl(oip->s_addr);
2176
2177 /*
2178 * Decide whether a bootp record is required.
2179 */
2180 dn.dn_flags = 0;
2181 DSVC_QEQ(query, DN_QFBOOTP_ONLY);
2182 if (is_bootp)
2183 dn.dn_flags = DN_FBOOTP_ONLY;
2184
2185 /*
2186 * These flags are used counter-intuitively.
2187 * This says that the setting of the bit
2188 * (off) in the dn.dn_flags matches the
2189 * setting in the record (off).
2190 */
2191 DSVC_QEQ(query, DN_QFUNUSABLE|DN_QFMANUAL);
2192
2193 for (retry = 0; !found && retry < 3; retry++) {
2194 now = time(NULL);
2195 (void) mutex_lock(&pnd->thr_mtx);
2196 nrecords = pnd->nthreads < DHCP_MIN_RECORDS ?
2197 DHCP_MIN_RECORDS : pnd->nthreads;
2198 (void) mutex_unlock(&pnd->thr_mtx);
2199
2200 /*
2201 * Purge cached records when expired or database
2202 * re-read.
2203 */
2204
2205 (void) mutex_lock(&pnd->free_mtx);
2206 dncp = pnd->freerec;
2207 if (dncp != NULL &&
2208 PND_FREE_TIMEOUT(pnd, now)) {
2209 pnd->freerec = NULL;
2210 dhcp_free_dd_list(pnd->dh, dncp);
2211 dncp = NULL;
2212 }
2213
2214 if (dncp != NULL) {
2215 if (retry == 0) {
2216 /* Try the next cached record */
2217 pnd->freerec = dncp->dnl_next;
2218 dncp->dnl_next = NULL;
2219 } else if (retry == 1) {
2220 /*
2221 * Try all remaining cached
2222 * records
2223 */
2224 pnd->freerec = NULL;
2225 }
2226 }
2227 if (retry > 1) {
2228 /* Try all possible records in datastore. */
2229 pnd->freerec = NULL;
2230 nrecords = -1;
2231 if (dncp != NULL) {
2232 dhcp_free_dd_list(
2233 pnd->dh, dncp);
2234 dncp = NULL;
2235 }
2236 }
2237 (void) mutex_unlock(&pnd->free_mtx);
2238
2239 io_done = (dncp == NULL);
2240 *dnlpp = dhcp_lookup_dd_classify(pcd->pnd,
2241 nrecords == -1 ? B_FALSE : B_TRUE, query,
2242 nrecords, &dn, (void **)&dncp,
2243 S_CID | S_FREE);
2244 if (*dnlpp != NULL) {
2245 dnp = (*dnlpp)->dnl_rec;
2246 tip.s_addr = htonl(dnp->dn_cip.s_addr);
2247 (void) update_offer(pcd, NULL, 0,
2248 &tip, B_TRUE);
2249 found = B_TRUE;
2250 }
2251
2252 (void) mutex_lock(&pnd->free_mtx);
2253 if (io_done) {
2254 /*
2255 * Note time when records were read.
2256 */
2257 if (dncp != NULL) {
2258 now = time(NULL);
2259 pnd->free_mtime = reinit_time;
2260 pnd->free_stamp = now +
2261 cache_secs;
2262 }
2263 }
2264
2265 /* Save any leftover records for later use. */
2266 if (dncp != NULL) {
2267 for (tlp = dncp;
2268 tlp != NULL && tlp->dnl_next;
2269 tlp = tlp->dnl_next)
2270 /* null statement */;
2271 tlp->dnl_next = pnd->freerec;
2272 pnd->freerec = dncp;
2273 }
2274 (void) mutex_unlock(&pnd->free_mtx);
2275 }
2276 }
2277 }
2278
2279 if (!found && !is_bootp) {
2280 /*
2281 * Struck out. No usable available addresses. Let's look for
2282 * the LRU expired address. Only makes sense for dhcp
2283 * clients. First we'll try the next record from
2284 * the lru list (this assumes lru database search capability).
2285 * Next we'll try all records. Finally we'll go get all
2286 * free records.
2287 *
2288 * Multi-threading: to improve performance, the following
2289 * algorithm coordinates accesses to the underlying table,
2290 * so only one thread is initiating lookups per network.
2291 * This is crucial, as lookup operations are expensive,
2292 * and not sufficiently malleable to allow partitioned
2293 * lookups (e.g. all that can be asked for are n free or
2294 * server-owned entries, multiple threads will retrieve
2295 * the same records).
2296 *
2297 * We only consider clients owned by us.
2298 * The outer loop runs through the server ips owned by us
2299 *
2300 * The three iterations through the inner loop attempt to use
2301 *
2302 * 1) the next cached entry
2303 * 2) all cached entries
2304 * 3) all free or per-server entries in the underlying table
2305 *
2306 * Since many threads are consuming the cached entries,
2307 * any thread may find itself in the role of having to
2308 * refresh the cache. We always read at least enough
2309 * entries to satisfy all current threads. Reading all
2310 * records is prohibitively expensive, and should only
2311 * be done as a last resort.
2312 *
2313 * As always, to better distribute garbage
2314 * collection and data structure aging tasks, each
2315 * thread must actively implement policy, checking
2316 * for offer expiration (which invalidates the cache).
2317 */
2318
2319 for (oip = owner_ip; oip->s_addr != INADDR_ANY; oip++) {
2320 /*
2321 * Initialize query.
2322 */
2323 DSVC_QINIT(query);
2324 DSVC_QEQ(query, DN_QSIP);
2325 dn.dn_sip.s_addr = ntohl(oip->s_addr);
2326
2327 /*
2328 * These flags are used counter-intuitively.
2329 * This says that the setting of the bit
2330 * (off) in the dn.dn_flags matches the
2331 * setting in the record (off).
2332 */
2333 DSVC_QEQ(query, DN_QFBOOTP_ONLY|
2334 DN_QFMANUAL|DN_QFUNUSABLE);
2335 dn.dn_flags = 0;
2336
2337 for (retry = 0; !found && retry < 3; retry++) {
2338 now = time(NULL);
2339 (void) mutex_lock(&pnd->thr_mtx);
2340 nrecords = pnd->nthreads < DHCP_MIN_RECORDS ?
2341 DHCP_MIN_RECORDS : pnd->nthreads;
2342 (void) mutex_unlock(&pnd->thr_mtx);
2343
2344 /*
2345 * Purge cached records when expired or database
2346 * re-read.
2347 */
2348
2349 (void) mutex_lock(&pnd->lru_mtx);
2350 dnsp = pnd->lrurec;
2351 if (dnsp != NULL && PND_LRU_TIMEOUT(pnd, now)) {
2352 pnd->lrurec = NULL;
2353 dhcp_free_dd_list(pnd->dh, dnsp);
2354 dnsp = NULL;
2355 }
2356
2357 if (dnsp != NULL) {
2358 if (retry == 0) {
2359 /* Try the next cached record */
2360 pnd->lrurec = dnsp->dnl_next;
2361 dnsp->dnl_next = NULL;
2362 } else if (retry == 1) {
2363 /*
2364 * Try all remaining cached
2365 * records
2366 */
2367 pnd->lrurec = NULL;
2368 }
2369 }
2370 if (retry > 1) {
2371 /* Try all possible records */
2372 pnd->lrurec = NULL;
2373 nrecords = -1;
2374 if (dnsp != NULL) {
2375 dhcp_free_dd_list(pnd->dh,
2376 dnsp);
2377 dnsp = NULL;
2378 }
2379 }
2380 (void) mutex_unlock(&pnd->lru_mtx);
2381
2382 io_done = (dnsp == NULL);
2383 *dnlpp = dhcp_lookup_dd_classify(pcd->pnd,
2384 nrecords == -1 ? B_FALSE : B_TRUE, query,
2385 nrecords, &dn, (void **)&dnsp, S_LRU);
2386 if (*dnlpp != NULL) {
2387 dnp = (*dnlpp)->dnl_rec;
2388 tip.s_addr = htonl(dnp->dn_cip.s_addr);
2389 (void) update_offer(pcd, NULL, 0, &tip,
2390 B_TRUE);
2391 found = B_TRUE;
2392 }
2393
2394 (void) mutex_lock(&pnd->lru_mtx);
2395 if (io_done) {
2396 if (dnsp != NULL) {
2397 now = time(NULL);
2398 pnd->lru_mtime = reinit_time;
2399 pnd->lru_stamp = now +
2400 cache_secs;
2401 }
2402 }
2403
2404 /*
2405 * Save any leftover records for possible
2406 * later use
2407 */
2408 if (dnsp != NULL) {
2409 for (tlp = dnsp;
2410 tlp != NULL && tlp->dnl_next;
2411 tlp = tlp->dnl_next)
2412 /* null statement */;
2413 tlp->dnl_next = pnd->lrurec;
2414 pnd->lrurec = dnsp;
2415 }
2416 (void) mutex_unlock(&pnd->lru_mtx);
2417 }
2418 }
2419 }
2420
2421 return (found);
2422 }
2423
2424 /*
2425 * purge_dnet_cache() - remove conflicting entries from the
2426 * free and lru dnet caches when records are modified. Expensive
2427 * but necessary.
2428 *
2429 * pnd - per net struct
2430 * dnp - pointer to cached/modified entry
2431 */
2432 static void
2433 purge_dnet_cache(dsvc_dnet_t *pnd, dn_rec_t *dnp)
2434 {
2435 dn_rec_list_t *tlp;
2436 dn_rec_list_t *plp;
2437
2438 (void) mutex_lock(&pnd->free_mtx);
2439
2440 for (plp = tlp = pnd->freerec; tlp != NULL; tlp = tlp->dnl_next) {
2441 if (tlp->dnl_rec->dn_cip.s_addr == dnp->dn_cip.s_addr) {
2442 if (tlp == plp) {
2443 pnd->freerec = tlp->dnl_next;
2444 } else {
2445 plp->dnl_next = tlp->dnl_next;
2446 }
2447 tlp->dnl_next = NULL;
2448 break;
2449 }
2450 plp = tlp;
2451 }
2452 (void) mutex_unlock(&pnd->free_mtx);
2453 if (tlp != NULL)
2454 dhcp_free_dd_list(pnd->dh, tlp);
2455
2456 (void) mutex_lock(&pnd->lru_mtx);
2457 for (plp = tlp = pnd->lrurec; tlp != NULL; tlp = tlp->dnl_next) {
2458 if (tlp->dnl_rec->dn_cip.s_addr == dnp->dn_cip.s_addr) {
2459 if (tlp == plp) {
2460 pnd->lrurec = tlp->dnl_next;
2461 } else {
2462 plp->dnl_next = tlp->dnl_next;
2463 }
2464 tlp->dnl_next = NULL;
2465 break;
2466 }
2467 plp = tlp;
2468 }
2469 (void) mutex_unlock(&pnd->lru_mtx);
2470 if (tlp != NULL)
2471 dhcp_free_dd_list(pnd->dh, tlp);
2472 }
2473
2474 /*
2475 * add_dnet_cache() - add a free entry back to the free dnet cache.
2476 *
2477 * Performance: this can greatly reduce the amount of work select_offer()
2478 * must perform.
2479 *
2480 * pnd - per net struct
2481 * dnlp - pointer to cached/modified entry.
2482 */
2483 static void
2484 add_dnet_cache(dsvc_dnet_t *pnd, dn_rec_list_t *dnlp)
2485 {
2486 (void) mutex_lock(&pnd->free_mtx);
2487 dnlp->dnl_next = pnd->freerec;
2488 pnd->freerec = dnlp;
2489 (void) mutex_unlock(&pnd->free_mtx);
2490 }
2491
2492 static char unowned_net[] = "the DHCP server believes the IP address that"
2493 " corresponds to the requested host name belongs to a network not"
2494 " managed by the DHCP server.\n";
2495 static char unowned_addr[] = "the DHCP server believes the IP address that"
2496 " corresponds to the requested host name is not managed by the DHCP"
2497 " server.\n";
2498
2499 /*
2500 * Determine whether the requested IP address is available to the requesting
2501 * client. To be so, its IP address must be managed by us, be on the ``right''
2502 * network and neither currently leased nor currently under offer to another
2503 * client.
2504 */
2505 static boolean_t
2506 addr_avail(dsvc_dnet_t *pnd, dsvc_clnt_t *pcd, dn_rec_list_t **dnlpp,
2507 struct in_addr req_ip, boolean_t isname)
2508 {
2509 dn_rec_t dn;
2510 dn_rec_list_t *dnip;
2511 uint32_t query;
2512
2513 *dnlpp = NULL;
2514 /*
2515 * first, check the ICMP list or offer list.
2516 */
2517 if (isname) {
2518 if (pcd->off_ip.s_addr != req_ip.s_addr &&
2519 check_offer(pnd, &req_ip) == B_FALSE) {
2520 /* Offered to someone else. Sorry. */
2521 dhcpmsg(LOG_DEBUG, "name_avail(F):"
2522 " check_offer failed\n");
2523 return (B_FALSE);
2524 }
2525 } else {
2526 if (update_offer(pcd, NULL, 0, &req_ip, B_TRUE) == B_FALSE) {
2527 /* Offered to someone else. Sorry. */
2528 if (isname) {
2529 dhcpmsg(LOG_DEBUG, "name_avail(F):"
2530 " check_other_offers failed\n");
2531 }
2532 return (B_FALSE);
2533 }
2534 }
2535
2536 /*
2537 * entry_available() searches for owner_ips
2538 * query on DN_QCIP will suffice here
2539 */
2540 DSVC_QINIT(query);
2541 DSVC_QEQ(query, DN_QCIP);
2542 dn.dn_cip.s_addr = ntohl(req_ip.s_addr);
2543
2544 dnip = NULL;
2545 *dnlpp = dhcp_lookup_dd_classify(pnd, B_FALSE, query, -1, &dn,
2546 (void **)&dnip, 0);
2547 dhcp_free_dd_list(pnd->dh, dnip);
2548 if (*dnlpp != NULL) {
2549 /*
2550 * Ok, the requested IP exists. But is it available?
2551 */
2552 if (!entry_available(pcd, (*dnlpp)->dnl_rec)) {
2553 dhcp_free_dd_list(pnd->dh, *dnlpp);
2554 *dnlpp = NULL;
2555 purge_offer(pcd, B_FALSE, B_TRUE);
2556 return (B_FALSE);
2557 }
2558 } else {
2559 if (isname)
2560 dhcpmsg(LOG_DEBUG, "name_avail(F): %s", unowned_addr);
2561 else
2562 purge_offer(pcd, B_FALSE, B_TRUE);
2563 return (B_FALSE);
2564 }
2565 return (B_TRUE);
2566 }
2567
2568 /*
2569 * Determine whether "name" is available. To be so, it must either not have
2570 * a corresponding IP address, or its IP address must be managed by us and
2571 * neither currently leased nor currently under offer to a client.
2572 *
2573 * To determine this, we first attempt to translate the name to an address.
2574 * If no name-to-address translation exists, it's automatically available.
2575 * Otherwise, we next check for any outstanding offers. Finally, we look
2576 * at the flags in the corresponding per-network table to see whether the
2577 * address is currently leased.
2578 *
2579 * Upon successful completion, we also return the vetted IP address as a
2580 * value result parameter.
2581 */
2582 static boolean_t
2583 name_avail(char *name, dsvc_clnt_t *pcd, PKT_LIST *plp, dn_rec_list_t **dnlpp,
2584 ENCODE *ecp, struct in_addr **iap)
2585 {
2586 struct hostent h, *hp, *owner_hp;
2587 char hbuf[NSS_BUFLEN_HOSTS];
2588 char fqname [NS_MAXDNAME+1];
2589 char owner [NS_MAXDNAME+1];
2590 int err, ho_len;
2591 struct in_addr ia, ma;
2592 dsvc_dnet_t *pnd;
2593 boolean_t isopen = B_FALSE;
2594 ENCODE *ep;
2595
2596 *dnlpp = NULL;
2597 /*
2598 * If possible, use a fully-qualified name to do the name-to-
2599 * address query. The complication is that the domain name
2600 * with which to qualify the client's host name resides in a
2601 * dhcptab macro unavailable at the time of the DHCPOFFER.
2602 * ecp will be non-NULL if we may have the means to fully-qualify
2603 * the name given.
2604 */
2605 if (strchr(name, '.') != NULL) {
2606 (void) strlcpy(fqname, name, sizeof (fqname));
2607 if (fqname[(strlen(fqname))-1] != '.')
2608 (void) strcat(fqname, ".");
2609 } else {
2610 /*
2611 * Append '.' domain-name '.' to hostname.
2612 * Note the use of the trailing '.' to avoid any surprises
2613 * because of the ndots value (see resolv.conf(4) for more
2614 * information about the latter).
2615 *
2616 * First see whether we can dredge up domain-name from the
2617 * ENCODE list.
2618 */
2619 if ((ecp != NULL) && ((ep = find_encode(ecp,
2620 DSYM_STANDARD, CD_DNSDOMAIN)) != NULL)) {
2621 DHCP_OPT *ho = plp->opts[CD_HOSTNAME];
2622
2623 /*
2624 * name_avail() should never be called unless the
2625 * CD_HOSTNAME option is present in the client's
2626 * packet.
2627 */
2628 assert(ho != NULL);
2629 ho_len = ho->len;
2630 if (ho->value[ho_len - 1] == '\0') {
2631 /* null at end of the hostname */
2632 ho_len = strlen((char *)ho->value);
2633 }
2634
2635 if (qualify_hostname(fqname, (char *)ho->value,
2636 (char *)ep->data, ho_len, ep->len) == -1)
2637 return (B_FALSE);
2638
2639 dhcpmsg(LOG_DEBUG, "name_avail: unqualified name\n"
2640 "found CD_DNSDOMAIN and qualified: %s\n", fqname);
2641 } else {
2642 /*
2643 * No DNS domain in the ENCODE list, have to use
2644 * local domain name.
2645 */
2646 if ((resolv_conf.defdname == NULL) ||
2647 (qualify_hostname(fqname, name,
2648 resolv_conf.defdname,
2649 strlen(name),
2650 strlen(resolv_conf.defdname)) == -1))
2651 return (B_FALSE);
2652
2653 dhcpmsg(LOG_DEBUG,
2654 "name_avail: unqualified name\n"
2655 "qualified with local domain: %s\n", fqname);
2656 }
2657 }
2658
2659 /*
2660 * Try a forward lookup on the requested name.
2661 * Consider the name available if we get a definitive
2662 * ``name doesn't exist'' indication.
2663 */
2664 hp = gethostbyname_r(fqname, &h, hbuf, sizeof (hbuf), &err);
2665 if (hp == NULL)
2666 if ((err == HOST_NOT_FOUND) || (err == NO_DATA)) {
2667 *iap = NULL;
2668 dhcpmsg(LOG_DEBUG,
2669 "name_avail(T): gethostbyname_r failed\n");
2670 return (B_TRUE);
2671 } else {
2672 dhcpmsg(LOG_DEBUG,
2673 "name_avail(F): gethostbyname_r failed, err %d\n",
2674 err);
2675 return (B_FALSE);
2676 }
2677
2678 /*
2679 * Check that the address has not been leased to someone else.
2680 * Bear in mind that there may be inactive A records in the DNS
2681 * (since we don't delete them when a lease expires or is released).
2682 * Try a reverse lookup on the address returned in hp.
2683 * If the owner of this address is different to the requested name
2684 * we can infer that owner is a stale A record.
2685 */
2686
2687 (void) memcpy(&ia, hp->h_addr, sizeof (struct in_addr));
2688 owner_hp = gethostbyaddr_r((char *)&ia, sizeof (struct in_addr),
2689 AF_INET, &h, hbuf, sizeof (hbuf), &err);
2690
2691 if (owner_hp == NULL) {
2692 /* If there's no PTR record the address can't be in use */
2693 if ((err == HOST_NOT_FOUND) || (err == NO_DATA)) {
2694 *iap = NULL;
2695 dhcpmsg(LOG_DEBUG,
2696 "name_avail(T): gethostbyaddr_r failed\n");
2697 return (B_TRUE);
2698 } else {
2699 dhcpmsg(LOG_DEBUG,
2700 "name_avail(F): gethostbyaddr_r failed\n");
2701 return (B_FALSE);
2702 }
2703 }
2704
2705 /* If name returned is not a FQDN, qualify with local domain name */
2706
2707 if (strchr(owner_hp->h_name, '.') != NULL) {
2708 (void) strlcpy(owner, owner_hp->h_name, sizeof (owner));
2709 if (owner[(strlen(owner))-1] != '.')
2710 (void) strcat(owner, ".");
2711 } else {
2712 if ((resolv_conf.defdname == NULL) ||
2713 (qualify_hostname(owner, owner_hp->h_name,
2714 resolv_conf.defdname,
2715 strlen(owner_hp->h_name),
2716 strlen(resolv_conf.defdname)) == -1))
2717 return (B_FALSE);
2718
2719 dhcpmsg(LOG_DEBUG,
2720 "name_avail: address owner qualified with %s\n",
2721 resolv_conf.defdname);
2722 }
2723
2724 if ((strncmp(owner, fqname, NS_MAXDNAME)) != 0) {
2725 /* Forward lookup found an inactive record - ignore it */
2726 *iap = NULL;
2727 dhcpmsg(LOG_DEBUG, "name_avail(T): 'A' record inactive: %s\n",
2728 owner);
2729 return (B_TRUE);
2730 }
2731
2732 /* Get pnd of the current client */
2733 pnd = pcd->pnd;
2734 get_netmask(&ia, &ma);
2735 if (pnd->net.s_addr != (ia.s_addr & ma.s_addr)) {
2736 /* get pnd of previous owner of the hostname */
2737 if (open_dnet(&pnd, &ia, &ma) != DSVC_SUCCESS) {
2738 /* we must not manage the net containing this address */
2739 dhcpmsg(LOG_DEBUG, "name_avail(F): %s", unowned_net);
2740 return (B_FALSE);
2741 }
2742 isopen = B_TRUE;
2743 }
2744
2745 /*
2746 * Test that the address has not been offered to someone else.
2747 */
2748 if (!addr_avail(pnd, pcd, dnlpp, ia, B_TRUE)) {
2749 if (isopen) {
2750 close_dnet(pnd, B_FALSE);
2751 }
2752 return (B_FALSE);
2753 }
2754 if (isopen)
2755 close_dnet(pnd, B_FALSE);
2756
2757 /* LINTED */
2758 **iap = *((struct in_addr *)hp->h_addr);
2759 dhcpmsg(LOG_DEBUG, "name_avail(T)\n");
2760 return (B_TRUE);
2761 }
2762
2763 static boolean_t
2764 entry_available(dsvc_clnt_t *pcd, dn_rec_t *dnp)
2765 {
2766 char ntoab[INET_ADDRSTRLEN];
2767 boolean_t isme = dnp->dn_cid_len == pcd->cid_len &&
2768 memcmp(pcd->cid, dnp->dn_cid, pcd->cid_len) == 0;
2769 (void) inet_ntop(AF_INET, &(dnp->dn_sip), ntoab, sizeof (ntoab));
2770
2771 if ((dnp->dn_flags & (DN_FMANUAL|DN_FUNUSABLE)) != 0) {
2772 dhcpmsg(LOG_DEBUG, "entry_available():"
2773 " %s is manually allocated or not usable\n",
2774 ntoab);
2775 return (B_FALSE);
2776 }
2777
2778 if (dnp->dn_cid_len != 0 && isme == B_FALSE &&
2779 (dnp->dn_flags & (DN_FAUTOMATIC|DN_FBOOTP_ONLY))) {
2780 dhcpmsg(LOG_DEBUG, "entry_available():"
2781 " %s is a permanent address or reserved for BOOTP\n",
2782 ntoab);
2783 return (B_FALSE);
2784 }
2785
2786 if (dnp->dn_cid_len != 0 && isme == B_FALSE &&
2787 (lease_t)time(NULL) < (lease_t)ntohl(dnp->dn_lease)) {
2788 dhcpmsg(LOG_DEBUG, "entry_available():"
2789 " lease on %s has not expired\n",
2790 ntoab);
2791 return (B_FALSE);
2792 }
2793
2794 if (match_ownerip(htonl(dnp->dn_sip.s_addr)) == NULL) {
2795 dhcpmsg(LOG_DEBUG, "entry_available():"
2796 " %s does not match owner_ip\n",
2797 ntoab);
2798 return (B_FALSE);
2799 }
2800
2801 /* Input IP is good. */
2802 return (B_TRUE);
2803 }
2804
2805 static char msft_classid[] = "MSFT ";
2806 static char no_domain[] = "name service update on behalf of client with ID"
2807 " %s failed because requested name was not fully-qualified and no DNS"
2808 " domain name was specified for this client in the dhcptab\n";
2809
2810 /*
2811 * Given a host name and IP address, try to do a host name update.
2812 */
2813 static boolean_t
2814 do_nsupdate(struct in_addr ia, ENCODE *ecp, PKT_LIST *plp)
2815 {
2816 struct hostent *hp;
2817 DHCP_OPT *ho;
2818 ENCODE *ep;
2819 char class_idbuf[DSYM_CLASS_SIZE];
2820 int puthostent_ret;
2821
2822 /*
2823 * hostent information is dynamically allocated so that threads spawned
2824 * by dns_puthostent() will have access to it after the calling thread
2825 * has returned.
2826 */
2827 hp = (struct hostent *)smalloc(sizeof (struct hostent));
2828 hp->h_addr_list = (char **)smalloc(2 * sizeof (char **));
2829 hp->h_addr_list[1] = NULL;
2830 hp->h_addr = smalloc(sizeof (struct in_addr));
2831 hp->h_aliases = NULL;
2832 hp->h_addrtype = AF_INET;
2833 hp->h_length = sizeof (struct in_addr);
2834 /*
2835 * Convert address to network order, as that's what hostent's are
2836 * expected to be.
2837 */
2838 /* LINTED */
2839 ((struct in_addr *)hp->h_addr)->s_addr = htonl(ia.s_addr);
2840
2841 /*
2842 * Is the host name unqualified? If so, try to qualify it. If that
2843 * can't be done, explain why the update won't be attempted.
2844 */
2845 ho = plp->opts[CD_HOSTNAME];
2846 if (memchr(ho->value, '.', ho->len) == NULL) {
2847 /*
2848 * See whether we can dredge up the DNS domain from the
2849 * ENCODE list.
2850 */
2851 if ((ep = find_encode(ecp, DSYM_STANDARD, CD_DNSDOMAIN)) !=
2852 NULL) {
2853 char *fqname;
2854 int ho_len = ho->len;
2855
2856 /*
2857 * We need room for
2858 *
2859 * hostname len +
2860 * strlen(".") +
2861 * domainname len +
2862 * strlen(".") +
2863 * trailing '\0'
2864 *
2865 * Note the use of the trailing '.' to avoid any
2866 * surprises because of the ndots value (see
2867 * resolv.conf(4) for more information about
2868 * the latter).
2869 */
2870 if (ho->value[ho_len - 1] == '\0') {
2871 ho_len = strlen((char *)ho->value);
2872 }
2873 fqname = smalloc(ho_len + ep->len + 1 + 1 + 1);
2874 /* first copy host name, ... */
2875 (void) memcpy(fqname, ho->value, ho_len);
2876 /* then '.', ... */
2877 (void) memcpy(fqname + ho_len, ".", 1);
2878 /* ... then domain name, */
2879 (void) memcpy(fqname + ho_len + 1, ep->data, ep->len);
2880 /* then a trailing '.', ... */
2881 (void) memcpy(fqname + ho_len + ep->len + 1, ".", 1);
2882 /* no need to null-terminate - smalloc() did it */
2883
2884 hp->h_name = fqname;
2885 dhcpmsg(LOG_DEBUG, "do_nsupdate: unqualified name\n"
2886 "found CD_DNSDOMAIN and qualified: %s\n", fqname);
2887 } else {
2888 char cidbuf[BUFSIZ];
2889
2890 (void) disp_cid(plp, cidbuf, sizeof (cidbuf));
2891 dhcpmsg(LOG_INFO, no_domain, cidbuf);
2892 }
2893 } else {
2894 hp->h_name = smalloc(ho->len + 1);
2895 (void) memcpy(hp->h_name, ho->value, ho->len);
2896 dhcpmsg(LOG_DEBUG, "do_nsupdate: fully qualified name: %s\n",
2897 hp->h_name);
2898 }
2899
2900 /* returns -1 or the number of name service updates done */
2901 puthostent_ret = dns_puthostent(hp, nsutimeout_secs);
2902 dhcpmsg(LOG_DEBUG, "do_nsupdate: dns_puthostent returned %d\n",
2903 puthostent_ret);
2904 if (puthostent_ret == -1) {
2905 return (B_FALSE);
2906 } else if (puthostent_ret == 0) {
2907 /*
2908 * dns_puthostent() didn't see any errors occur,
2909 * but no updates were done; Microsoft clients
2910 * (i.e. clients with a Microsoft class ID) expect
2911 * it to succeed, so we lie to them.
2912 */
2913 if (((get_class_id(plp, class_idbuf,
2914 sizeof (class_idbuf))) != NULL) &&
2915 (strncmp(msft_classid, class_idbuf,
2916 sizeof (msft_classid)) == 0)) {
2917 dhcpmsg(LOG_DEBUG, "do_nsupdate: class ID \"%s\"\n",
2918 class_idbuf);
2919 return (B_TRUE);
2920 } else
2921 return (B_FALSE);
2922 } else {
2923 return (B_TRUE);
2924 }
2925 }