xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/per_dnet.c (revision 8027:0ed4c2d79627)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <sys/types.h>
32 #include <sys/sysmacros.h>
33 #include <assert.h>
34 #include <errno.h>
35 #include <syslog.h>
36 #include <fcntl.h>
37 #include <sys/stat.h>
38 #include <sys/mman.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <netinet/in_systm.h>
42 #include <arpa/inet.h>
43 #include <net/if.h>
44 #include <netinet/dhcp.h>
45 #include "dhcpd.h"
46 #include "interfaces.h"
47 #include <locale.h>
48 
49 /*
50  * This file contains the access routines for the dhcp databases.
51  */
52 
53 static dsvc_dnet_t *get_dnet(struct in_addr *);
54 static boolean_t unhash_dnet(dsvc_dnet_t *, boolean_t);
55 static int dnet_cmp(dsvc_dnet_t *, dsvc_dnet_t *);
56 static dsvc_clnt_t *get_client(hash_tbl *, uchar_t *, uchar_t);
57 static int clnt_cmp(dsvc_clnt_t *, dsvc_clnt_t *);
58 static boolean_t unhash_clnt(dsvc_clnt_t *, boolean_t);
59 static boolean_t unhash_offer(dsvc_clnt_t *, boolean_t);
60 
61 static hash_tbl	*ntable;		/* global per net datastore table */
62 
63 /*
64  * Initialize global per network hash table.
65  *
66  * Per-bucket rwlocks reduce lock contention between interface and
67  * client threads.
68  *
69  * Performance: dynamically calculate hash table size.
70  */
71 int
initntab(void)72 initntab(void)
73 {
74 	char **listppp;
75 	uint_t cnt = 0;
76 	uint_t ind;
77 
78 	if (list_dd(&datastore, DSVC_DHCPNETWORK, &listppp, &cnt) ==
79 	    DSVC_SUCCESS) {
80 		if (listppp) {
81 			for (ind = 0; listppp[ind] != NULL && ind < cnt;
82 			    ind++)
83 				free(listppp[ind]);
84 			free(listppp);
85 		}
86 	}
87 	ntable = hash_Init(cnt, unhash_dnet, MAX(net_thresh, clnt_thresh),
88 	    B_TRUE);
89 	return (ntable == NULL ? -1 : 0);
90 }
91 
92 /*
93  * open_dnet: Open the appropriate dhcp database given a network address and
94  * a subnet mask. These in_addr's are expected in network order.
95  *
96  * Returns: DSVC_SUCCESS for success or dsvc error.
97  */
98 int
open_dnet(dsvc_dnet_t ** pndp,struct in_addr * net,struct in_addr * mask)99 open_dnet(dsvc_dnet_t **pndp, struct in_addr *net, struct in_addr *mask)
100 {
101 	int		err;
102 	dsvc_dnet_t	*pnd;
103 	struct in_addr	datum;
104 	int		hsize = 0;
105 	uint32_t	query;
106 	dn_rec_t	dn;
107 	uint_t		count;
108 	struct in_addr	*oip;
109 
110 	datum.s_addr = net->s_addr;
111 	datum.s_addr &= mask->s_addr;
112 
113 	*pndp = NULL;
114 	/* Locate existing dnet. */
115 	if ((pnd = get_dnet(&datum)) != NULL) {
116 		(void) mutex_lock(&pnd->pnd_mtx);
117 		if ((pnd->flags & DHCP_PND_ERROR) != 0) {
118 			(void) mutex_unlock(&pnd->pnd_mtx);
119 			close_dnet(pnd, B_TRUE);
120 			return (DSVC_INTERNAL);
121 		} else if ((pnd->flags & DHCP_PND_CLOSING) != 0) {
122 			(void) mutex_unlock(&pnd->pnd_mtx);
123 			close_dnet(pnd, B_FALSE);
124 			return (DSVC_BUSY);
125 		} else {
126 			(void) mutex_unlock(&pnd->pnd_mtx);
127 			*pndp = pnd;
128 			return (DSVC_SUCCESS);
129 		}
130 	}
131 
132 	/* Allocate new dnet. */
133 
134 	pnd = (dsvc_dnet_t *)smalloc(sizeof (dsvc_dnet_t));
135 	pnd->net.s_addr = datum.s_addr;
136 	pnd->subnet.s_addr = mask != 0 ? mask->s_addr : htonl(INADDR_ANY);
137 	(void) inet_ntop(AF_INET, &datum, pnd->network, sizeof (pnd->network));
138 
139 	/* Allocate hash tables. */
140 	if (max_clients != -1)
141 		hsize = max_clients;
142 	if ((pnd->ctable =
143 	    hash_Init(hsize, unhash_clnt, MAX(off_secs, clnt_thresh),
144 	    B_TRUE)) == NULL) {
145 		free(pnd);
146 		return (DSVC_INTERNAL);
147 	}
148 	if ((pnd->itable =
149 	    hash_Init(hsize, unhash_offer, off_secs, B_TRUE)) == NULL) {
150 		free(pnd->ctable);
151 		free(pnd);
152 		return (DSVC_INTERNAL);
153 	}
154 
155 	err = dhcp_open_dd(&pnd->dh, &datastore, DSVC_DHCPNETWORK, pnd->network,
156 	    DSVC_READ|DSVC_WRITE);
157 
158 	if (err != DSVC_SUCCESS) {
159 		free(pnd->ctable);
160 		free(pnd->itable);
161 		free(pnd);
162 		return (err);
163 	}
164 
165 	/* Find out how many addresses the server owns in this datastore */
166 
167 	pnd->naddrs = 0;
168 	for (oip = owner_ip; oip->s_addr != INADDR_ANY; oip++) {
169 
170 		DSVC_QINIT(query);
171 		DSVC_QEQ(query, DN_QSIP);
172 		dn.dn_sip.s_addr = ntohl(oip->s_addr);
173 
174 		err = lookup_dd(pnd->dh, B_FALSE, query, -1, &dn, NULL, &count);
175 
176 		if (err != DSVC_SUCCESS) {
177 			free(pnd->ctable);
178 			free(pnd->itable);
179 			free(pnd);
180 			return (err);
181 		}
182 
183 		pnd->naddrs += count;
184 	}
185 
186 	if ((pnd->hand = hash_Insert(ntable, &pnd->net, sizeof (struct in_addr),
187 	    dnet_cmp, pnd, pnd)) == NULL) {
188 		/* Another thread has begun work on this net. */
189 #ifdef	DEBUG
190 		dhcpmsg(LOG_DEBUG, "Duplicate network: %s\n", pnd->network);
191 #endif	/* DEBUG */
192 		free(pnd->ctable);
193 		free(pnd->itable);
194 		free(pnd);
195 		return (DSVC_BUSY);
196 	}
197 
198 	(void) mutex_init(&pnd->pnd_mtx, USYNC_THREAD, NULL);
199 	(void) mutex_init(&pnd->thr_mtx, USYNC_THREAD, NULL);
200 	(void) mutex_init(&pnd->free_mtx, USYNC_THREAD, NULL);
201 	(void) mutex_init(&pnd->lru_mtx, USYNC_THREAD, NULL);
202 	(void) mutex_init(&pnd->lrupage_mtx, USYNC_THREAD, NULL);
203 
204 	*pndp = pnd;
205 	return (DSVC_SUCCESS);
206 }
207 
208 /*
209  * close_dnet: Closes specified dhcp-network database.
210  *
211  * delete - immediately delete.
212  */
213 void
close_dnet(dsvc_dnet_t * pnd,boolean_t delete)214 close_dnet(dsvc_dnet_t *pnd, boolean_t delete)
215 {
216 	hash_Rele(pnd->hand, delete);
217 }
218 
219 /*
220  * get_dnet: Given a network name, look it up in the hash table.
221  * Returns ptr to dsvc_dnet_t structure, NULL if error occurs.
222  */
223 static dsvc_dnet_t *
get_dnet(struct in_addr * netp)224 get_dnet(struct in_addr *netp)
225 {
226 	dsvc_dnet_t tpnd;
227 	dsvc_dnet_t *pnd;
228 
229 	tpnd.net.s_addr = netp->s_addr;
230 	pnd = (dsvc_dnet_t *)hash_Lookup(ntable, netp,
231 	    sizeof (struct in_addr), dnet_cmp, &tpnd, B_TRUE);
232 
233 	/* refresh pnd hash entry timer */
234 	if (pnd != NULL)
235 		hash_Dtime(pnd->hand, time(NULL) + ntable->dfree_time);
236 	return (pnd);
237 }
238 
239 /*
240  * unhash_dnet: Free a datastore reference.
241  *
242  * Aging in hash routines will trigger freeing of unused references.
243  */
244 /*ARGSUSED*/
245 static boolean_t
unhash_dnet(dsvc_dnet_t * pnd,boolean_t force)246 unhash_dnet(dsvc_dnet_t *pnd, boolean_t force)
247 {
248 	int		err = 0;
249 	dsvc_pendclnt_t	*workp;
250 	dsvc_thr_t	*thrp;
251 	timestruc_t	tm;
252 	int		nthreads;
253 	int		refcnt;
254 
255 	if (pnd == NULL)
256 		return (B_FALSE);
257 
258 	/* Mark as closing. */
259 	(void) mutex_lock(&pnd->pnd_mtx);
260 	pnd->flags |= DHCP_PND_CLOSING;
261 	(void) mutex_unlock(&pnd->pnd_mtx);
262 
263 	/*
264 	 * Wait for any remaining thread(s) to exit.
265 	 */
266 	refcnt = hash_Refcount(pnd->hand);
267 
268 	(void) mutex_lock(&pnd->thr_mtx);
269 	nthreads = pnd->nthreads;
270 	while (nthreads > 0 || refcnt > 0) {
271 		/*
272 		 * Wait for 1ms to avoid stalling monitor threads.
273 		 * cond_wait() not used to avoid thread synchronization
274 		 * overhead.
275 		 */
276 		tm.tv_sec = 0;
277 		tm.tv_nsec = 1000 * 10;
278 		(void) cond_reltimedwait(&pnd->thr_cv, &pnd->thr_mtx, &tm);
279 		nthreads = pnd->nthreads;
280 		(void) mutex_unlock(&pnd->thr_mtx);
281 		/* Threads will exit. */
282 		for (thrp = pnd->thrhead; thrp; thrp = thrp->thr_next) {
283 			(void) mutex_lock(&thrp->thr_mtx);
284 			thrp->thr_flags |= DHCP_THR_EXITING;
285 			(void) mutex_unlock(&thrp->thr_mtx);
286 			(void) cond_signal(&thrp->thr_cv);
287 		}
288 		refcnt = hash_Refcount(pnd->hand);
289 		(void) mutex_lock(&pnd->thr_mtx);
290 	}
291 
292 	/* Free threads. */
293 	while ((thrp = pnd->thrhead) != NULL) {
294 		pnd->thrhead = pnd->thrhead->thr_next;
295 		(void) mutex_destroy(&thrp->thr_mtx);
296 		free(thrp);
297 	}
298 	pnd->thrtail = NULL;
299 
300 	/* Free deferred thread work. */
301 	while ((workp = pnd->workhead) != NULL) {
302 		pnd->workhead = pnd->workhead->pnd_next;
303 		free(workp);
304 	}
305 	pnd->worktail = NULL;
306 	(void) mutex_unlock(&pnd->thr_mtx);
307 
308 	/* Free clients. */
309 	if (pnd->ctable) {
310 		hash_Reset(pnd->ctable, unhash_clnt);
311 		free(pnd->ctable);
312 	}
313 
314 	/* Free cached datastore records. */
315 	(void) mutex_lock(&pnd->free_mtx);
316 	if (pnd->freerec != NULL)
317 		dhcp_free_dd_list(pnd->dh, pnd->freerec);
318 	(void) mutex_unlock(&pnd->free_mtx);
319 
320 	(void) mutex_lock(&pnd->lru_mtx);
321 	if (pnd->lrurec != NULL)
322 		dhcp_free_dd_list(pnd->dh, pnd->lrurec);
323 	(void) mutex_unlock(&pnd->lru_mtx);
324 
325 	if (pnd->itable) {
326 		hash_Reset(pnd->itable, unhash_offer);
327 		free(pnd->itable);
328 	}
329 
330 	(void) mutex_lock(&pnd->lrupage_mtx);
331 	if (pnd->lrupage) {
332 		free(pnd->lrupage);
333 		pnd->lrupage = NULL;
334 	}
335 	(void) mutex_unlock(&pnd->lrupage_mtx);
336 
337 	if (pnd->dh != NULL) {
338 		if (dhcp_close_dd(&pnd->dh) != DSVC_SUCCESS) {
339 			dhcpmsg(LOG_ERR,
340 			    "Error %d while closing for network %s\n",
341 			    err, pnd->network);
342 		}
343 		pnd->dh = NULL;
344 	}
345 
346 	(void) mutex_destroy(&pnd->pnd_mtx);
347 	(void) mutex_destroy(&pnd->thr_mtx);
348 	(void) mutex_destroy(&pnd->free_mtx);
349 	(void) mutex_destroy(&pnd->lru_mtx);
350 	(void) mutex_destroy(&pnd->lrupage_mtx);
351 	free(pnd);
352 
353 	return (B_TRUE);
354 }
355 
356 /*
357  * dnet_cmp: Compare datastore references by network address.
358  */
359 static int
dnet_cmp(dsvc_dnet_t * m1,dsvc_dnet_t * m2)360 dnet_cmp(dsvc_dnet_t *m1, dsvc_dnet_t *m2)
361 {
362 	return (m1->net.s_addr == m2->net.s_addr);
363 }
364 
365 /*
366  * open_clnt: Open the appropriate dhcp client given a network
367  * database and client id.
368  *
369  * Returns: DSVC_SUCCESS for success or errno if an error occurs.
370  *
371  * pnd - per net struct
372  * pcdp - client struct returned here
373  * cid - clientid
374  * cid_len - cid length
375  * nocreate - if set, client struct must previously exist
376  */
377 int
open_clnt(dsvc_dnet_t * pnd,dsvc_clnt_t ** pcdp,uchar_t * cid,uchar_t cid_len,boolean_t nocreate)378 open_clnt(dsvc_dnet_t *pnd, dsvc_clnt_t **pcdp, uchar_t *cid,
379 	uchar_t cid_len, boolean_t nocreate)
380 {
381 	dsvc_clnt_t	*pcd;
382 	time_t		now;
383 	uint_t		blen;
384 
385 	*pcdp = NULL;
386 
387 	/* Network is closing. */
388 	if ((pnd->flags & DHCP_PND_CLOSING) != 0)
389 		return (DSVC_BUSY);
390 
391 	/* Locate existing client. */
392 	if ((pcd = get_client(pnd->ctable, cid, cid_len)) != NULL) {
393 		(void) mutex_lock(&pcd->pcd_mtx);
394 		/* Client is closing - temporarily busy. */
395 		if ((pcd->flags & DHCP_PCD_CLOSING) != 0) {
396 			(void) mutex_unlock(&pcd->pcd_mtx);
397 			close_clnt(pcd, B_FALSE);
398 			return (DSVC_BUSY);
399 		}
400 		(void) mutex_unlock(&pcd->pcd_mtx);
401 		*pcdp = pcd;
402 		return (DSVC_SUCCESS);
403 	}
404 	if (nocreate == B_TRUE)
405 		return (DSVC_NOENT);
406 
407 	/* Allocate new client. */
408 	(void) mutex_lock(&pnd->thr_mtx);
409 	if (max_clients != -1) {
410 		now = time(NULL);
411 		/*
412 		 * Performance/DOS: dsvc_clnt_t structs are normally
413 		 * freed when the protocol conversation completes,
414 		 * or when garbage collected (see hash.c). In
415 		 * certain error scenarios (e.g. DOS attacks, or
416 		 * network failures where large numbers of clients
417 		 * begin protocol conversations that never complete)
418 		 * the server will become unresponsive. To detect
419 		 * these scenarios, free slot time is observed, and
420 		 * after a grace period (2 * the offer time the currently
421 		 * allocated clients are allowed), clients are randomly
422 		 * deleted.
423 		 */
424 		if (pnd->nclients < max_clients) {
425 			/* Keep track of last time there were free slots. */
426 			pnd->clnt_stamp = now;
427 			(void) mutex_unlock(&pnd->thr_mtx);
428 		} else if (pnd->clnt_stamp + off_secs > now) {
429 			/* Wait for other clients to complete. */
430 			(void) mutex_unlock(&pnd->thr_mtx);
431 			return (DSVC_INTERNAL);
432 		} else {
433 			/* Forcibly delete a client to free a slot. */
434 			pnd->clnt_stamp = now;
435 			(void) mutex_unlock(&pnd->thr_mtx);
436 			hash_Reap(pnd->ctable, unhash_clnt);
437 		}
438 	} else
439 		(void) mutex_unlock(&pnd->thr_mtx);
440 
441 	pcd = (dsvc_clnt_t *)smalloc(sizeof (dsvc_clnt_t));
442 	(void) mutex_init(&pcd->pcd_mtx, USYNC_THREAD, NULL);
443 	(void) mutex_init(&pcd->pkt_mtx, USYNC_THREAD, NULL);
444 	(void) mutex_lock(&pcd->pcd_mtx);
445 	pcd->pkthead = pcd->pkttail = NULL;
446 	pcd->pnd = pnd;
447 	(void) memcpy(pcd->cid, cid, cid_len);
448 	pcd->cid_len = cid_len;
449 	blen = sizeof (pcd->cidbuf);
450 	(void) octet_to_hexascii(cid, cid_len, pcd->cidbuf, &blen);
451 
452 	if ((pcd->chand = hash_Insert(pnd->ctable, cid, cid_len, clnt_cmp,
453 	    pcd, pcd)) == NULL) {
454 		/* Another thread has begun work on this client */
455 #ifdef	DEBUG
456 		dhcpmsg(LOG_DEBUG, "Duplicate client\n");
457 #endif	/* DEBUG */
458 		(void) mutex_unlock(&pcd->pcd_mtx);
459 		(void) mutex_destroy(&pcd->pcd_mtx);
460 		(void) mutex_destroy(&pcd->pkt_mtx);
461 		free(pcd);
462 		return (DSVC_BUSY);
463 	}
464 	(void) mutex_unlock(&pcd->pcd_mtx);
465 	(void) mutex_lock(&pnd->thr_mtx);
466 	pnd->nclients++;
467 	(void) mutex_unlock(&pnd->thr_mtx);
468 	*pcdp = pcd;
469 	return (DSVC_SUCCESS);
470 }
471 
472 /*
473  * close_clnt: Closes specified client.
474  *
475  * delete - immediately delete.
476  */
477 void
close_clnt(dsvc_clnt_t * pcd,boolean_t delete)478 close_clnt(dsvc_clnt_t *pcd, boolean_t delete)
479 {
480 	hash_Rele(pcd->chand, delete);
481 }
482 
483 /*
484  * get_client: Given a client name, look it up in the per client hash table.
485  * Returns ptr to dsvc_clnt_t structure, NULL if error occurs.
486  */
487 static dsvc_clnt_t *
get_client(hash_tbl * table,uchar_t * cid,uchar_t cid_len)488 get_client(hash_tbl *table, uchar_t *cid, uchar_t cid_len)
489 {
490 	dsvc_clnt_t tpcd;
491 	dsvc_clnt_t *pcd;
492 
493 	(void) memcpy(tpcd.cid, cid, cid_len);
494 	tpcd.cid_len = cid_len;
495 
496 	pcd = (dsvc_clnt_t *)hash_Lookup(table, cid, cid_len, clnt_cmp,
497 	    &tpcd, B_TRUE);
498 
499 	/* refresh client hash entry's timer */
500 	if (pcd != NULL) {
501 		(void) mutex_lock(&pcd->pcd_mtx);
502 		hash_Dtime(pcd->chand, time(NULL) + table->dfree_time);
503 		(void) mutex_unlock(&pcd->pcd_mtx);
504 	}
505 	return (pcd);
506 }
507 
508 /*
509  * unhash_clnt: Free a client structure.
510  *
511  * Aging in hash routines will trigger freeing of unused references.
512  */
513 /*ARGSUSED*/
514 static boolean_t
unhash_clnt(dsvc_clnt_t * pcd,boolean_t force)515 unhash_clnt(dsvc_clnt_t *pcd, boolean_t force)
516 {
517 	dsvc_dnet_t	*pnd = pcd->pnd;
518 	timestruc_t	tm;
519 	int		refcnt;
520 	struct in_addr	off_ip;
521 
522 
523 	refcnt = hash_Refcount(pcd->chand);
524 
525 	/*
526 	 * Wait for thread(s) accessing pcd to drop references.
527 	 */
528 	(void) mutex_lock(&pcd->pcd_mtx);
529 	pcd->flags |= DHCP_PCD_CLOSING;	/* client no longer usable... */
530 	while (pcd->clnt_thread != NULL || refcnt > 0) {
531 		/*
532 		 * Wait for 1ms to avoid stalling monitor threads.
533 		 * cond_wait() not used to avoid thread synchronization
534 		 * overhead.
535 		 */
536 		tm.tv_sec = 0;
537 		tm.tv_nsec = 1000 * 10;
538 		(void) cond_reltimedwait(&pcd->pcd_cv, &pcd->pcd_mtx, &tm);
539 		(void) mutex_unlock(&pcd->pcd_mtx);
540 		refcnt = hash_Refcount(pcd->chand);
541 		(void) mutex_lock(&pcd->pcd_mtx);
542 	}
543 
544 	if (pcd->pkthead != NULL)
545 		free_pktlist(pcd);
546 
547 	off_ip.s_addr = pcd->off_ip.s_addr;
548 	(void) mutex_unlock(&pcd->pcd_mtx);
549 
550 	if (off_ip.s_addr != htonl(INADDR_ANY))
551 		purge_offer(pcd, B_TRUE, B_FALSE);
552 
553 	if (pcd->dnlp != NULL)
554 		dhcp_free_dd_list(pnd->dh, pcd->dnlp);
555 
556 	(void) mutex_destroy(&pcd->pcd_mtx);
557 	(void) mutex_destroy(&pcd->pkt_mtx);
558 	free(pcd);
559 
560 	(void) mutex_lock(&pnd->thr_mtx);
561 	pnd->nclients--;
562 	(void) mutex_unlock(&pnd->thr_mtx);
563 
564 	return (B_TRUE);
565 }
566 
567 /*
568  * unhash_offer: Free offer associated with a client structure.
569  *
570  * Aging in hash routines will trigger freeing of expired offers.
571  */
572 static boolean_t
unhash_offer(dsvc_clnt_t * pcd,boolean_t force)573 unhash_offer(dsvc_clnt_t *pcd, boolean_t force)
574 {
575 	IF		*ifp = pcd->ifp;
576 	boolean_t	ret = B_TRUE;
577 	char		ntoab[INET_ADDRSTRLEN];
578 
579 	(void) mutex_lock(&pcd->pcd_mtx);
580 	if (pcd->off_ip.s_addr != htonl(INADDR_ANY) &&
581 	    PCD_OFFER_TIMEOUT(pcd, time(NULL))) {
582 		if (pcd->clnt_thread == NULL) {
583 			if (debug)
584 				dhcpmsg(LOG_INFO, "Freeing offer: %s\n",
585 				    inet_ntop(AF_INET, &pcd->off_ip,
586 				    ntoab, sizeof (ntoab)));
587 			pcd->off_ip.s_addr = htonl(INADDR_ANY);
588 			(void) mutex_unlock(&pcd->pcd_mtx);
589 			(void) mutex_lock(&ifp->ifp_mtx);
590 			if (ifp->offers > 0)
591 				ifp->offers--;
592 			ifp->expired++;
593 			(void) mutex_unlock(&ifp->ifp_mtx);
594 		} else if (force == B_FALSE) {
595 			/*
596 			 * Worker thread is currently active. To avoid
597 			 * unnecessary thread synchronization, defer
598 			 * freeing the offer until the worker thread has
599 			 * completed.
600 			 */
601 			(void) mutex_unlock(&pcd->pcd_mtx);
602 			hash_Age(pcd->ihand);
603 			ret = B_FALSE;
604 		} else
605 			(void) mutex_unlock(&pcd->pcd_mtx);
606 	} else
607 		(void) mutex_unlock(&pcd->pcd_mtx);
608 	return (ret);
609 }
610 
611 /*
612  * clnt_cmp: Compare client structures by cid.
613  */
614 static int
clnt_cmp(dsvc_clnt_t * m1,dsvc_clnt_t * m2)615 clnt_cmp(dsvc_clnt_t *m1, dsvc_clnt_t *m2)
616 {
617 	return (m1->cid_len == m2->cid_len &&
618 	    memcmp((char *)m1->cid, (char *)m2->cid, m1->cid_len) == 0);
619 }
620 
621 /*
622  * clnt_netcmp Compare clients by network address. This is used to maintain
623  * the itable hash table of client addresses.
624  */
625 int
clnt_netcmp(dsvc_clnt_t * d1,dsvc_clnt_t * d2)626 clnt_netcmp(dsvc_clnt_t *d1, dsvc_clnt_t *d2)
627 {
628 	return (d1->off_ip.s_addr == d2->off_ip.s_addr);
629 }
630 
631 /*
632  * close_clnts: Free the ntable hash table and associated client structs.
633  * Table walk frees each per network and client struct.
634  */
635 void
close_clnts(void)636 close_clnts(void)
637 {
638 	hash_Reset(ntable, unhash_dnet);
639 }
640