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
  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
  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
 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 *
 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
 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
 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
 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
 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 *
 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
 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
 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
 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
 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
 636 close_clnts(void)
 637 {
 638         hash_Reset(ntable, unhash_dnet);
 639 }