xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/generic.c (revision 9785:d6463ea666d6)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * This file contains routines that are shared between the DHCP server
29  * implementation and BOOTP server compatibility.
30  */
31 
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <assert.h>
37 #include <sys/types.h>
38 #include <stdarg.h>
39 #include <errno.h>
40 #include <alloca.h>
41 #include <sys/socket.h>
42 #include <sys/sockio.h>
43 #include <net/if.h>
44 #include <sys/syslog.h>
45 #include <string.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #include <netdb.h>
49 #include <netinet/dhcp.h>
50 #include <search.h>
51 #include <dhcp_symbol.h>
52 #include "dhcpd.h"
53 #include "per_dnet.h"
54 #include "interfaces.h"
55 #include <locale.h>
56 #include <resolv.h>
57 
58 /*
59  * Get the client id. Sets cid and len.
60  */
61 void
get_clnt_id(PKT_LIST * plp,uchar_t * cid,int cidlen,uchar_t * len)62 get_clnt_id(PKT_LIST *plp, uchar_t *cid, int cidlen,  uchar_t *len)
63 {
64 	DHCP_OPT *optp = plp->opts[CD_CLIENT_ID];
65 
66 	/*
67 	 * If the DHCP client specified the client id option, use that,
68 	 * otherwise use the client's hardware type and hardware address.
69 	 */
70 	if (plp->opts[CD_DHCP_TYPE] != NULL && optp != NULL) {
71 		/* DHCP client w/ client id */
72 		if (cidlen < optp->len)
73 			*len = (uchar_t)cidlen;
74 		else
75 			*len = optp->len;
76 		(void) memcpy(cid, optp->value, *len);
77 	} else {
78 		/* BOOTP client or DHCP client w/o client id. */
79 		*cid++ = plp->pkt->htype;
80 		*len = plp->pkt->hlen + 1;
81 		if (cidlen < *len)
82 			*len = cidlen;
83 		(void) memcpy(cid, plp->pkt->chaddr, *len);
84 	}
85 }
86 
87 /*
88  * Return a string representing an ASCII version of the client_id.
89  */
90 char *
disp_cid(PKT_LIST * plp,char * bufp,int len)91 disp_cid(PKT_LIST *plp, char *bufp, int len)
92 {
93 	DHCP_OPT	*optp = plp->opts[CD_CLIENT_ID];
94 	uchar_t	*cp;
95 	uchar_t cplen;
96 	uint_t tlen;
97 
98 	if (optp != (DHCP_OPT *)0) {
99 		cp =  optp->value;
100 		cplen = optp->len;
101 	} else {
102 		cp = plp->pkt->chaddr;
103 		cplen =  plp->pkt->hlen;
104 	}
105 
106 	tlen = len;
107 	(void) octet_to_hexascii(cp, cplen, bufp, &tlen);
108 	return (bufp);
109 }
110 
111 /*
112  * Based on the contents of the PKT_LIST structure for an incoming
113  * packet, determine the net address and subnet mask identifying the
114  * dhcp-network database. This centralizes choices that were formerly
115  * made in the specific protocol routines.
116  */
117 void
determine_network(IF * ifp,PKT_LIST * plp,struct in_addr * netp,struct in_addr * subp)118 determine_network(IF *ifp, PKT_LIST *plp, struct in_addr *netp,
119     struct in_addr *subp)
120 {
121 	/*
122 	 * For BOOTP, REQUEST, RELEASE, and INFORM packets, trust client's
123 	 * notion of IP address if ciaddr is set. Use it to figure out correct
124 	 * dhcp-network.
125 	 */
126 	netp->s_addr = plp->pkt->ciaddr.s_addr;
127 	if (netp->s_addr != htonl(INADDR_ANY) &&
128 	    (plp->opts[CD_DHCP_TYPE] == NULL ||
129 	    (*plp->opts[CD_DHCP_TYPE]->value == REQUEST ||
130 	    *plp->opts[CD_DHCP_TYPE]->value == RELEASE ||
131 	    *plp->opts[CD_DHCP_TYPE]->value == INFORM))) {
132 		/*
133 		 * Calculate client's default net mask, consult netmasks
134 		 * database to see if net is further subnetted. Use resulting
135 		 * subnet mask with client's address to produce dhcp-network
136 		 * database name.
137 		 */
138 		get_netmask(netp, subp);
139 	} else
140 		netp->s_addr = htonl(INADDR_ANY);
141 
142 	/*
143 	 * If no trusted IP address, examine giaddr.
144 	 */
145 	if (netp->s_addr == htonl(INADDR_ANY)) {
146 		if (plp->pkt->giaddr.s_addr != htonl(INADDR_ANY)) {
147 			netp->s_addr = plp->pkt->giaddr.s_addr;
148 			/*
149 			 * Packet received thru a relay agent. Calculate the
150 			 * net's address using subnet mask and giaddr.
151 			 */
152 			get_netmask(netp, subp);
153 		} else {
154 			/* Locally connected net. */
155 			netp->s_addr = ifp->addr.s_addr;
156 			subp->s_addr = ifp->mask.s_addr;
157 		}
158 	}
159 }
160 
161 struct netmask_node;
162 
163 typedef struct netmask_node {
164 	struct in_addr net;			/* cached network */
165 	struct in_addr mask;			/* cached netmask */
166 } NNODE;
167 
168 static void		*nroot;			/* root of netmask tree */
169 static time_t		nroot_mtime;		/* time for dynamic free */
170 static time_t		nroot_stamp;		/* time for dynamic free */
171 static rwlock_t		nroot_rwlock;		/* synchronization variable */
172 
173 /*
174  * nm_cmp() - determine whether key n1 is within range of net/mask n2
175  */
176 static int
nm_cmp(const void * n1,const void * n2)177 nm_cmp(const void *n1, const void *n2)
178 {
179 	void *v1 = (void *) (((NNODE *)n1)->net.s_addr &
180 	    ((NNODE *)n2)->mask.s_addr);
181 	void *v2 = (void *) ((NNODE *)n2)->net.s_addr;
182 
183 	return (memcmp(&v1, &v2, sizeof (struct in_addr)));
184 }
185 
186 /*
187  * Given a network-order address, calculate client's default net mask.
188  * Consult local cache, then netmasks database to see if net is further
189  * subnetted. We'll only snag the first netmask that matches our criteria.
190  */
191 void
get_netmask(struct in_addr * n_addrp,struct in_addr * s_addrp)192 get_netmask(struct in_addr *n_addrp, struct in_addr *s_addrp)
193 {
194 	NNODE key;
195 	NNODE *node;
196 	NNODE **ret;
197 	struct in_addr haddr;
198 
199 	assert(n_addrp != NULL && s_addrp != NULL);
200 
201 	/*
202 	 * First check locally maintained, incomplete cache.
203 	 */
204 	(void) rw_rdlock(&nroot_rwlock);
205 	if (nroot != NULL) {
206 		/* Delete expired tree. */
207 		if (nroot_mtime != reinit_time || nroot_stamp < time(NULL)) {
208 			(void) rw_unlock(&nroot_rwlock);
209 			(void) rw_wrlock(&nroot_rwlock);
210 			while ((ret = (NNODE **)nroot) != NULL) {
211 				node = *ret;
212 				(void) tdelete(node, &nroot, nm_cmp);
213 				free(node);
214 			}
215 			nroot_mtime = reinit_time;
216 			nroot_stamp = time(NULL) + DHCP_NSS_TIME;
217 		} else {
218 			key.net.s_addr = ntohl(n_addrp->s_addr);
219 			key.mask.s_addr = INADDR_ANY;
220 			if ((ret = (NNODE **)tfind((void *)&key,
221 			    (void * const *)&nroot, nm_cmp)) != NULL) {
222 				s_addrp->s_addr = htonl((*ret)->mask.s_addr);
223 				(void) rw_unlock(&nroot_rwlock);
224 				return;
225 			}
226 		}
227 	}
228 
229 	/*
230 	 * Note: workaround for 4336124: single-thread access to
231 	 * nss search routines to avoid getting incorrect results.
232 	 */
233 	node = (NNODE *)smalloc(sizeof (NNODE));
234 
235 	/* Convert to and from host order. */
236 	haddr.s_addr = ntohl(n_addrp->s_addr);
237 	get_netmask4(&haddr, s_addrp);
238 	node->mask.s_addr = s_addrp->s_addr;
239 	node->net.s_addr = haddr.s_addr & node->mask.s_addr;
240 	s_addrp->s_addr = htonl(s_addrp->s_addr);
241 
242 	/* While inserting check that another insert has not occurred. */
243 	ret = (NNODE **)tsearch((void *)node, &nroot, nm_cmp);
244 	if (ret != NULL && *ret != node)
245 		free(node);
246 
247 	(void) rw_unlock(&nroot_rwlock);
248 }
249 
250 /*
251  * This function is charged with loading the options field with the
252  * configured and/or asked for options. Note that if the packet is too
253  * small to fit the options, then option overload is enabled.
254  *
255  * Note that the caller is expected to free any allocated ENCODE lists,
256  * with the exception of locally-allocated lists in the case where ecp is
257  * NULL, but vecp is not. In this case, the resultant ecp list (ecp == tvep)
258  * is freed locally.
259  *
260  * Returns: The actual size of the utilized packet buffer.
261  */
262 
263 int
load_options(int flags,PKT_LIST * c_plp,PKT * r_pktp,int replen,uchar_t * optp,ENCODE * ecp,ENCODE * vecp)264 load_options(int flags, PKT_LIST *c_plp, PKT *r_pktp, int replen, uchar_t *optp,
265     ENCODE *ecp, ENCODE *vecp)
266 {
267 	ENCODE		*ep, *prevep, *tvep = NULL;
268 	ENCODE		*router_ecp = NULL;
269 	PKT		*c_pktp = c_plp->pkt;
270 	uchar_t		cat;
271 	ushort_t	code;
272 	uint_t		vend_len;
273 	uchar_t		len, *vp, *vdata, *data, *endp, *main_optp, *opt_endp;
274 	uchar_t		overload = DHCP_OVRLD_CLR;
275 	uchar_t		using_overload = DHCP_OVRLD_CLR;
276 	boolean_t	srv_using_file = B_FALSE, clnt_ovrld_file = B_FALSE;
277 	boolean_t	echo_clnt_file;
278 
279 	if (c_plp->opts[CD_OPTION_OVERLOAD] != NULL &&
280 	    *c_plp->opts[CD_OPTION_OVERLOAD]->value & DHCP_OVRLD_FILE)
281 		clnt_ovrld_file = B_TRUE;
282 
283 	opt_endp = (uchar_t *)((uint_t)r_pktp->options + replen -
284 	    BASE_PKT_SIZE);
285 	endp = opt_endp;
286 
287 	/*
288 	 * We handle vendor options by fabricating an ENCODE of type
289 	 * CD_VENDOR_SPEC, and setting its datafield equal to vecp.
290 	 *
291 	 * We assume we've been handed the proper class list.
292 	 */
293 	if (vecp != NULL && (flags & DHCP_NON_RFC1048) == 0) {
294 		vend_len = 0;
295 		for (ep = vecp, vend_len = 0; ep != NULL; ep = ep->next)
296 			vend_len += (ep->len + 2);
297 
298 		if (vend_len != 0) {
299 			if (vend_len > (uint_t)0xff) {
300 				dhcpmsg(LOG_WARNING,
301 				    "Warning: Too much vendor data (> 255) to "
302 				    "encapsulate within option %d.\n",
303 				    CD_VENDOR_SPEC);
304 				vend_len = (uint_t)0xff;
305 			}
306 			vdata = (uchar_t *)smalloc(vend_len);
307 
308 			for (vp = vdata, tvep = vecp; tvep != NULL &&
309 			    (uchar_t *)(vp + tvep->len + 2) <= &vdata[vend_len];
310 			    tvep = tvep->next) {
311 				*vp++ = tvep->code;
312 				*vp++ = tvep->len;
313 				(void) memcpy(vp, tvep->data, tvep->len);
314 				vp += tvep->len;
315 			}
316 
317 			/* this make_encode *doesn't* copy data */
318 			tvep = make_encode(DSYM_VENDOR, CD_VENDOR_SPEC,
319 			    vend_len, vdata, ENC_DONT_COPY);
320 
321 			/* Tack it on the end of standard list. */
322 			for (ep = prevep = ecp; ep != NULL; ep = ep->next)
323 				prevep = ep;
324 			if (prevep != NULL)
325 				prevep->next = tvep;
326 			else
327 				ecp = tvep;
328 		}
329 	}
330 
331 	/*
332 	 * Scan the options first to determine if we could potentially
333 	 * option overload.
334 	 */
335 	if (flags & DHCP_DHCP_CLNT) {
336 		for (ep = ecp; ep != NULL; ep = ep->next) {
337 			if (ep->category == DSYM_FIELD)
338 				switch (ep->code) {
339 				case CD_SNAME:
340 					overload |= DHCP_OVRLD_SNAME;
341 					break;
342 				case CD_BOOTFILE:
343 					overload |= DHCP_OVRLD_FILE;
344 					srv_using_file = B_TRUE;
345 					break;
346 				}
347 		}
348 	} else {
349 		/* BOOTP uses these fields for fixed parameters, no overload */
350 		overload = DHCP_OVRLD_ALL;
351 	}
352 
353 	if (c_pktp->file[0] != '\0' && !clnt_ovrld_file && !srv_using_file) {
354 		/*
355 		 * simply echo back client's boot file, and don't overload.
356 		 * if CD_BOOTPATH is set, we'll simply rewrite the r_pktp
357 		 * file field to include it along with the client's requested
358 		 * name during the load pass through the internal options.
359 		 * Here we let the overload code know we're not to overload
360 		 * the file field.
361 		 */
362 		(void) memcpy(r_pktp->file, c_pktp->file,
363 		    sizeof (r_pktp->file));
364 		overload |= DHCP_OVRLD_FILE;
365 		echo_clnt_file = B_TRUE;
366 	} else
367 		echo_clnt_file = B_FALSE;
368 
369 	/* Now actually load the options! */
370 	for (ep = ecp; ep != NULL; ep = ep->next) {
371 		cat = ep->category;
372 		code = ep->code;
373 		len = ep->len;
374 		data = ep->data;
375 
376 		/*
377 		 * non rfc1048 clients can only get packet fields and
378 		 * the CD_BOOTPATH internal pseudo opt, which only potentially
379 		 * affects the file field.
380 		 */
381 		if ((flags & DHCP_NON_RFC1048) &&
382 		    !(cat == DSYM_FIELD || (cat == DSYM_INTERNAL &&
383 		    code == CD_BOOTPATH))) {
384 			continue;
385 		}
386 
387 		if ((flags & DHCP_SEND_LEASE) == 0 &&
388 		    cat == DSYM_STANDARD &&
389 		    (code == CD_T1_TIME || code == CD_T2_TIME ||
390 		    code == CD_LEASE_TIME)) {
391 			continue;
392 		}
393 
394 		/* standard and site options */
395 		if (cat == DSYM_STANDARD || cat == DSYM_SITE ||
396 		    cat == DSYM_VENDOR) {
397 
398 			uchar_t	*need_optp;
399 
400 			/*
401 			 * This horrible kludge is necessary because the DHCP
402 			 * options RFCs require that the subnet option MUST
403 			 * precede the router option.  To accomplish this, we
404 			 *
405 			 *	inspect each of the standard options, waiting
406 			 *	for CD_ROUTER to turn up (if it never does,
407 			 *	no special handling is needed)
408 			 *
409 			 *	search the remaining options for CD_SUBNETMASK
410 			 *	If it occurs, we
411 			 *		set router_ecp to indicate where to find
412 			 *		the router option's values that we have
413 			 *		not yet emitted
414 			 *
415 			 *		reinitialize code, len, and data to emit
416 			 *		the CD_SUBNETMASK option now
417 			 *
418 			 *		when CD_SUBNETMASK is encountered, we
419 			 *		reinitialize code, len, and data to emit
420 			 *		the CD_ROUTER option
421 			 */
422 			if ((cat == DSYM_STANDARD) && (code == CD_ROUTER)) {
423 				ENCODE *tp;
424 
425 				for (tp = ep->next; tp != NULL; tp = tp->next)
426 					if ((tp->category == DSYM_STANDARD) &&
427 					    (tp->code == CD_SUBNETMASK)) {
428 						router_ecp = ep;
429 						code = CD_SUBNETMASK;
430 						len = tp->len;
431 						data = tp->data;
432 					}
433 			} else if ((cat == DSYM_STANDARD) &&
434 			    (code == CD_SUBNETMASK) && (router_ecp != NULL)) {
435 				code = CD_ROUTER;
436 				len = router_ecp->len;
437 				data = router_ecp->data;
438 			}
439 
440 			/*
441 			 * Keep an eye on option field. Option overload. Note
442 			 * that we need to keep track of the space necessary
443 			 * to place the Overload option in the options section
444 			 * (that's the 3 octets below.) The 2 octets cover the
445 			 * necessary code and len portion of the payload.
446 			 */
447 			if (using_overload == DHCP_OVRLD_CLR) {
448 				/* 2 for code/len, 3 for overload option */
449 				need_optp = &optp[len + 2 + 3];
450 			} else {
451 				/* Just need 2 for code/len */
452 				need_optp = &optp[len + 2];
453 			}
454 			if (need_optp > endp) {
455 				/*
456 				 * If overload is not possible, we will
457 				 * keep going, hoping to find an option
458 				 * that will fit in the remaining space,
459 				 * rather than just give up.
460 				 */
461 				if (overload != DHCP_OVRLD_ALL) {
462 					if (using_overload == DHCP_OVRLD_CLR) {
463 						*optp++ = CD_OPTION_OVERLOAD;
464 						*optp++ = 1;
465 						main_optp = optp;
466 					} else {
467 						if (optp < endp)
468 							*optp = CD_END;
469 						overload |= using_overload;
470 					}
471 				}
472 				switch (overload) {
473 				case DHCP_OVRLD_CLR:
474 					/* great, can use both */
475 					/* FALLTHRU */
476 				case DHCP_OVRLD_FILE:
477 					/* Can use sname. */
478 					optp = r_pktp->sname;
479 					endp = r_pktp->file;
480 					using_overload |= DHCP_OVRLD_SNAME;
481 					break;
482 				case DHCP_OVRLD_SNAME:
483 					/* Using sname, can use file. */
484 					optp = r_pktp->file;
485 					endp = r_pktp->cookie;
486 					using_overload |= DHCP_OVRLD_FILE;
487 					break;
488 				}
489 			}
490 			/* Skip the option if it's too long to fit */
491 			if (len < (endp - optp - 1)) {
492 				/* Load options. */
493 				*optp++ = (uchar_t)code;
494 				*optp++ = len;
495 				(void) memcpy(optp, data, len);
496 				optp += len;
497 			}
498 		} else if (cat == DSYM_FIELD) {
499 			/* packet field pseudo options */
500 			switch (code) {
501 			case CD_SIADDR:
502 				/*
503 				 * Configuration includes Boot server addr
504 				 */
505 				(void) memcpy((void *)&r_pktp->siaddr, data,
506 				    len);
507 				break;
508 			case CD_SNAME:
509 				/*
510 				 * Configuration includes Boot server name
511 				 */
512 				(void) memcpy(r_pktp->sname, data, len);
513 				break;
514 			case CD_BOOTFILE:
515 				/*
516 				 * Configuration includes boot file.
517 				 * Always authoritative.
518 				 */
519 				(void) memset(r_pktp->file, 0,
520 				    sizeof (r_pktp->file));
521 				(void) memcpy(r_pktp->file, data, len);
522 				break;
523 			default:
524 				dhcpmsg(LOG_ERR,
525 				    "Unsettable DHCP packet field: %d\n", code);
526 				break;
527 			}
528 		} else if (cat == DSYM_INTERNAL) {
529 			/* Internal server pseudo options */
530 			switch (code) {
531 			case CD_BOOTPATH:
532 				/*
533 				 * Prefix for boot file. Only used if
534 				 * client provides bootfile and server doesn't
535 				 * specify one. Prepended on client's bootfile
536 				 * value. Otherwise ignored.
537 				 */
538 				if (echo_clnt_file) {
539 					uchar_t alen, flen;
540 
541 					alen = sizeof (c_pktp->file);
542 					flen = alen - 1;
543 					if (c_pktp->file[flen] != '\0')
544 						flen++;
545 					else
546 						flen = strlen(
547 						    (char *)c_pktp->file);
548 
549 					if ((len + flen + 1) > alen) {
550 						char *bp = alloca(alen + 1);
551 						char *bf = alloca(alen + 1);
552 						(void) memcpy(bp, data, len);
553 						bp[len] = '\0';
554 						(void) memcpy(bf, c_pktp->file,
555 						    flen);
556 						bf[flen] = '\0';
557 						dhcpmsg(LOG_ERR,
558 						    "BootPath(%1$s) + "
559 						    "BootFile(%2$s) too "
560 						    "long: %3$d > %4$d\n",
561 						    bp, bf, (len + flen), alen);
562 					} else {
563 						(void) memcpy(r_pktp->file,
564 						    data, len);
565 						r_pktp->file[len] = '/';
566 						(void) memcpy(
567 						    &r_pktp->file[len + 1],
568 						    c_pktp->file, flen);
569 					}
570 				}
571 				break;
572 			case CD_BOOL_HOSTNAME:
573 				/* FALLTHRU */
574 			case CD_BOOL_LEASENEG:
575 				/* FALLTHRU */
576 			case CD_BOOL_ECHO_VCLASS:
577 				/*
578 				 * These pseudo opts have had their
579 				 * affect elsewhere, such as dhcp.c.
580 				 */
581 				break;
582 			default:
583 				dhcpmsg(LOG_ERR,
584 				    "Unknown Internal pseudo opt: %d\n", code);
585 				break;
586 			}
587 		} else {
588 			dhcpmsg(LOG_ERR,
589 			    "Unrecognized option with code: %d %d\n", cat,
590 			    code);
591 		}
592 	}
593 
594 	if (using_overload != DHCP_OVRLD_CLR) {
595 		*main_optp++ = using_overload;
596 		if (optp < endp)
597 			*optp = CD_END;
598 	} else
599 		main_optp = optp;	/* no overload */
600 
601 	if (main_optp < opt_endp)
602 		*main_optp++ = CD_END;
603 
604 	if (ecp == tvep)
605 		free_encode_list(ecp);
606 
607 	return (BASE_PKT_SIZE + (uint_t)(main_optp - r_pktp->options));
608 }
609 
610 /*
611  * Reinitialize the dhcptab database, as a result of timeout or
612  * user signal. Note: if_head_mtx cannot be held by caller.
613  */
614 void *
reinitialize(void * arg)615 reinitialize(void *arg)
616 {
617 	int	totpkts;
618 	IF	*ifp;
619 	thread_t *tp = (thread_t *)arg;
620 	int err;
621 
622 	/*
623 	 * Got a signal to reinitialize
624 	 */
625 
626 	if (verbose)
627 		dhcpmsg(LOG_INFO, "Reinitializing server\n");
628 
629 	if (!no_dhcptab) {
630 		if (checktab() != 0) {
631 			dhcpmsg(LOG_WARNING,
632 			    "WARNING: Cannot access dhcptab.\n");
633 		} else {
634 			if ((err = readtab(PRESERVE_DHCPTAB)) != 0) {
635 				dhcpmsg(LOG_ERR,
636 				    "Error reading dhcptab.\n");
637 				return ((void *)err);
638 			}
639 		}
640 	}
641 
642 	/*
643 	 * Drop all pending offers, display interface statistics.
644 	 */
645 	if (verbose) {
646 		(void) mutex_lock(&if_head_mtx);
647 		for (ifp = if_head, totpkts = 0; ifp != NULL; ifp = ifp->next) {
648 			(void) mutex_lock(&ifp->ifp_mtx);
649 			disp_if_stats(ifp);
650 			totpkts += ifp->received;
651 			(void) mutex_unlock(&ifp->ifp_mtx);
652 		}
653 		(void) mutex_unlock(&if_head_mtx);
654 
655 		dhcpmsg(LOG_INFO,
656 		    "Total Packets received on all interfaces: %d\n", totpkts);
657 		dhcpmsg(LOG_INFO, "Server reinitialized.\n");
658 	}
659 
660 	/* Default domain may have changed */
661 	if (res_ninit(&resolv_conf) == -1)
662 		dhcpmsg(LOG_ERR, "Cannot acquire resolver configuration.\n");
663 
664 	/* Release reinitialization thread */
665 	reinit_time = time(NULL);
666 	*tp = NULL;
667 	thr_exit(NULL);
668 
669 	return (NULL);
670 }
671