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
  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 *
  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
 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
 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
 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
 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 *
 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 }