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 2010 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #include <stdio.h>
  28 #include <stdlib.h>
  29 #include <unistd.h>
  30 #include <stdarg.h>
  31 #include <sys/types.h>
  32 #include <sys/time.h>
  33 #include <fcntl.h>
  34 #include <errno.h>
  35 #include <assert.h>
  36 #include <string.h>
  37 #include <sys/socket.h>
  38 #include <sys/sockio.h>
  39 #include <net/if.h>
  40 #include <net/if_arp.h>
  41 #include <netinet/in_systm.h>
  42 #include <netinet/in.h>
  43 #include <arpa/inet.h>
  44 #include <netinet/if_ether.h>
  45 #include <netinet/ip.h>
  46 #include <netinet/udp.h>
  47 #include <stropts.h>
  48 #include <stdio.h>
  49 #include <ctype.h>
  50 #include <syslog.h>
  51 #include <netinet/dhcp.h>
  52 #include <dhcp_symbol.h>
  53 #include "dhcpd.h"
  54 #include "per_dnet.h"
  55 #include "interfaces.h"
  56 #include <v4_sum_impl.h>
  57 #include <locale.h>
  58 
  59 static int socksize = 64 * 1024;        /* large socket window size for data */
  60 static const uchar_t magic_cookie[] = BOOTMAGIC;
  61 static void disp_if(IF *);
  62 
  63 /*
  64  * Network interface configuration. This file contains routines which
  65  * handle the input side of the DHCP/BOOTP/Relay agent. Multiple interfaces
  66  * are handled by identifying explicitly each interface, and creating a
  67  * stream for each. If only one usable interface exists, then a "normal"
  68  * UDP socket is used for simplicity's sake.
  69  */
  70 
  71 IF      *if_head;               /* head of interfaces list */
  72 mutex_t if_head_mtx;            /* mutex for adding/deleting IF list entries */
  73 char    *interfaces;            /* user specified interfaces */
  74 static int      num_interfaces; /* # of usable interfaces on the system */
  75 
  76 static char *
  77 dsrvr_socktype(dsrvr_socktype_t stype)
  78 {
  79         char *rp;
  80 
  81         switch (stype) {
  82         case DSRVR_LBCAST:
  83                 rp = "limited broadcast";
  84                 break;
  85         case DSRVR_DBCAST:
  86                 rp = "directed broadcast";
  87                 break;
  88         case DSRVR_UCAST:
  89                 rp = "unicast";
  90                 break;
  91         }
  92         return (rp);
  93 }
  94 
  95 /*
  96  * Given two packets, match them based on BOOTP header operation, packet len,
  97  * hardware type, flags, ciaddr, DHCP type, client id, or chaddr.
  98  * Returns B_TRUE if they match, B_FALSE otherwise.
  99  */
 100 static boolean_t
 101 match_plp(PKT_LIST *alp, PKT_LIST *blp)
 102 {
 103         DHCP_OPT        *a, *b;
 104 
 105         assert(alp != NULL && blp != NULL);
 106 
 107         if (alp->pkt->op != blp->pkt->op ||
 108             alp->len != alp->len ||
 109             alp->pkt->htype != blp->pkt->htype ||
 110             alp->pkt->flags != blp->pkt->flags ||
 111             alp->pkt->ciaddr.s_addr != blp->pkt->ciaddr.s_addr)
 112                 return (B_FALSE);       /* not even the same BOOTP type. */
 113 
 114 #ifdef DEBUG
 115         if (alp->pkt->giaddr.s_addr != blp->pkt->giaddr.s_addr) {
 116                 dhcpmsg(LOG_DEBUG,
 117                     "%04d match_plp: giaddr mismatch on 0x%x, 0x%x\n",
 118                     thr_self());
 119         }
 120 #endif  /* DEBUG */
 121 
 122         a = alp->opts[CD_DHCP_TYPE];
 123         b = blp->opts[CD_DHCP_TYPE];
 124         if (a == NULL && b == NULL) {
 125                 /* bootp */
 126                 if (memcmp(alp->pkt->chaddr, blp->pkt->chaddr,
 127                     alp->pkt->hlen) == 0)
 128                         return (B_TRUE);
 129         } else if (a != NULL && b != NULL) {
 130                 if (a->value[0] == b->value[0]) {
 131                         /* dhcp - packet types match. */
 132                         a = alp->opts[CD_CLIENT_ID];
 133                         b = blp->opts[CD_CLIENT_ID];
 134                         if (a != NULL && b != NULL) {
 135                                 if (memcmp(a->value, b->value, a->len) == 0)
 136                                         return (B_TRUE);
 137                         } else {
 138                                 if (memcmp(alp->pkt->chaddr, blp->pkt->chaddr,
 139                                     alp->pkt->hlen) == 0)
 140                                         return (B_TRUE);
 141                         }
 142                 }
 143         }
 144         return (B_FALSE);
 145 }
 146 
 147 /*
 148  * Given a packet, searches for a later packet in the
 149  * interface's client list. If the search is successful, the argument
 150  * packet is deleted, and the later packet is returned with the appropriate
 151  * fields/options modified.
 152  *
 153  * Matches are based on match_plp(). The list is scanned until the final packet
 154  * which "matches" is found. The last match replaces
 155  * the argument plp. Duplicates are deleted.
 156  *
 157  * General Notes: After the first candidate is found, the list is checked to
 158  * the tail of the list for other matches. For each packet  which is deleted.
 159  * the duplicate statistic is incremented for each one. If no candidate is
 160  * found, then the argument plp is returned.
 161  *
 162  * Caveats: What about length and contents of packets? By definition, a
 163  * client is not supposed to be altering this between frames, so we should
 164  * be ok. Since the argument plp may be destroyed, it is assumed to be
 165  * detached.
 166  */
 167 PKT_LIST *
 168 refresh_pktlist(dsvc_clnt_t *pcd, PKT_LIST *plp)
 169 {
 170         PKT_LIST        *wplp, *tplp, *retplp = NULL;
 171         IF              *ifp = pcd->ifp;
 172 
 173         assert(MUTEX_HELD(&pcd->pkt_mtx));
 174 
 175         wplp = pcd->pkthead;
 176         while (wplp != NULL) {
 177                 if (match_plp(plp, wplp)) {
 178                         pcd->pending--;
 179 
 180                         (void) mutex_lock(&ifp->ifp_mtx);
 181                         ifp->duplicate++;
 182                         (void) mutex_unlock(&ifp->ifp_mtx);
 183 
 184                         /*
 185                          * Note that tplp, retplp can be synonyms for
 186                          * wplp. The synonyms are used because moldy plp's
 187                          * will be nuked, and the plp to return will be
 188                          * detached.
 189                          */
 190                         tplp = wplp;
 191                         wplp = wplp->next;
 192 
 193                         if (retplp != NULL) {
 194                                 /* moldy duplicates */
 195                                 free_plp(retplp);
 196                         }
 197                         retplp = tplp;
 198                         detach_plp(pcd, retplp);
 199                 } else {
 200                         wplp = wplp->next;
 201                 }
 202         }
 203 
 204         if (retplp == NULL)
 205                 retplp = plp;
 206         else {
 207                 if (debug) {
 208                         dhcpmsg(LOG_DEBUG,
 209                             "%04d: Refreshed (0x%p) to (0x%p)\n",
 210                             thr_self(), (void *)plp, (void *)retplp);
 211                 }
 212                 free_plp(plp);
 213         }
 214 
 215         return (retplp);
 216 }
 217 
 218 /*
 219  * Queries the IP transport layer for configured interfaces. Those that
 220  * are acceptable for use by our daemon have these characteristics:
 221  *
 222  *      Not loopback
 223  *      Is UP
 224  *
 225  * Sets num_interfaces global to number of valid, selected interfaces.
 226  *
 227  * Returns: 0 for success, the appropriate errno on fatal failure.
 228  *
 229  * Notes: Code gleaned from the in.rarpd, solaris 2.2.
 230  */
 231 static int
 232 find_interfaces(void)
 233 {
 234         int                     i, k, ip, reqsize, numifs;
 235         boolean_t               found;
 236         ushort_t                mtu_tmp;
 237         struct ifreq            *reqbuf, *ifr;
 238         struct ifconf           ifconf;
 239         IF                      *ifp, *if_tail;
 240         struct sockaddr_in      *sin;
 241         char                    **user_if;
 242         ENCODE                  *hecp;
 243 
 244         if ((ip = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
 245                 dhcpmsg(LOG_ERR, "Error opening socket: %s\n",
 246                     strerror(errno));
 247                 return (1);
 248         }
 249 
 250         if (ioctl(ip, SIOCGIFNUM, &numifs) < 0) {
 251                 dhcpmsg(LOG_WARNING,
 252                     "Error discovering number of network interfaces: %s\n",
 253                     strerror(errno));
 254                 (void) close(ip);
 255                 return (1);
 256         }
 257 
 258         reqsize = numifs * sizeof (struct ifreq);
 259         reqbuf = (struct ifreq *)smalloc(reqsize);
 260 
 261         ifconf.ifc_len = reqsize;
 262         ifconf.ifc_buf = (caddr_t)reqbuf;
 263 
 264         if (ioctl(ip, SIOCGIFCONF, &ifconf) < 0) {
 265                 dhcpmsg(LOG_ERR,
 266                     "Error getting network interface information: %s\n",
 267                     strerror(errno));
 268                 free(reqbuf);
 269                 (void) close(ip);
 270                 return (1);
 271         }
 272 
 273         /*
 274          * Verify that user specified interfaces are valid.
 275          */
 276         user_if = (char **)smalloc(numifs * sizeof (char *));
 277         if (interfaces != NULL) {
 278                 for (i = 0; i < numifs; i++) {
 279                         user_if[i] = strtok(interfaces, ",");
 280                         if (user_if[i] == NULL)
 281                                 break;          /* we're done */
 282                         interfaces = NULL; /* for next call to strtok() */
 283 
 284                         for (found = B_FALSE, ifr = ifconf.ifc_req;
 285                             ifr < &ifconf.ifc_req[ifconf.ifc_len /
 286                             sizeof (struct ifreq)]; ifr++) {
 287                                 if (strcmp(user_if[i], ifr->ifr_name) == 0) {
 288                                         found = B_TRUE;
 289                                         break;
 290                                 }
 291                         }
 292                         if (!found) {
 293                                 dhcpmsg(LOG_ERR,
 294                                     "Invalid network interface:  %s\n",
 295                                     user_if[i]);
 296                                 free(reqbuf);
 297                                 free(user_if);
 298                                 (void) close(ip);
 299                                 return (1);
 300                         }
 301                 }
 302                 if (i < numifs)
 303                         user_if[i] = NULL;
 304         } else
 305                 user_if[0] = NULL;
 306 
 307         /*
 308          * For each interface, build an interface structure. Ignore any
 309          * LOOPBACK or down interfaces.
 310          */
 311         if_tail = if_head = NULL;
 312         for (ifr = ifconf.ifc_req;
 313             ifr < &ifconf.ifc_req[ifconf.ifc_len / sizeof (struct ifreq)];
 314             ifr++) {
 315                 if (ioctl(ip, SIOCGIFFLAGS, ifr) < 0) {
 316                         dhcpmsg(LOG_ERR,
 317 "Error encountered getting interface: %s flags: %s\n",
 318                             ifr->ifr_name, strerror(errno));
 319                         continue;
 320                 }
 321                 if ((ifr->ifr_flags & IFF_LOOPBACK) ||
 322                     !(ifr->ifr_flags & IFF_UP))
 323                         continue;
 324 
 325                 num_interfaces++;       /* all possible interfaces counted */
 326 
 327                 /*
 328                  * If the user specified a list of interfaces,
 329                  * we'll only consider the ones specified.
 330                  */
 331                 if (user_if[0] != NULL) {
 332                         for (i = 0; i < numifs; i++) {
 333                                 if (user_if[i] == NULL)
 334                                         break; /* skip this interface */
 335                                 if (strcmp(user_if[i], ifr->ifr_name) == 0)
 336                                         break;  /* user wants this one */
 337                         }
 338                         if (i == numifs || user_if[i] == NULL)
 339                                 continue;       /* skip this interface */
 340                 } else if (strchr(ifr->ifr_name, ':') != NULL)
 341                         continue;       /* skip virtual interfaces */
 342 
 343                 ifp = (IF *)smalloc(sizeof (IF));
 344                 (void) strcpy(ifp->nm, ifr->ifr_name);
 345 
 346                 ifp->ifceno = if_nametoindex(ifp->nm);
 347                 ifp->flags = ifr->ifr_flags;
 348                 for (k = 0; k < DSRVR_NUM_DESC; k++)
 349                         ifp->descs[k] = -1;
 350 
 351                 /*
 352                  * Broadcast address. Not valid for POINTOPOINT
 353                  * connections.
 354                  */
 355                 if ((ifp->flags & IFF_POINTOPOINT) == 0) {
 356                         if (ifp->flags & IFF_BROADCAST) {
 357                                 if (ioctl(ip, SIOCGIFBRDADDR, ifr) < 0) {
 358                                         dhcpmsg(LOG_ERR, "Error encountered \
 359 getting interface: %s broadcast address: %s\n", ifp->nm, strerror(errno));
 360                                         free(ifp);
 361                                         num_interfaces--;
 362                                         continue;
 363                                 }
 364                                 /* LINTED [alignment ok] */
 365                                 sin = (struct sockaddr_in *)&ifr->ifr_addr;
 366                                 ifp->bcast = sin->sin_addr;
 367                         } else
 368                                 ifp->bcast.s_addr = htonl(INADDR_ANY);
 369 
 370                         hecp = make_encode(DSYM_STANDARD, CD_BROADCASTADDR,
 371                             sizeof (struct in_addr), &ifp->bcast,
 372                             ENC_COPY);
 373                         replace_encode(&ifp->ecp, hecp, ENC_DONT_COPY);
 374                 }
 375 
 376                 /* Subnet mask */
 377                 if (ioctl(ip, SIOCGIFNETMASK, ifr) < 0) {
 378                         dhcpmsg(LOG_ERR, "Error encountered getting \
 379 interface: %s netmask: %s\n", ifp->nm, strerror(errno));
 380                         free_encode_list(ifp->ecp);
 381                         free(ifp);
 382                         num_interfaces--;
 383                         continue;
 384                 }
 385                 /* LINTED [alignment ok] */
 386                 sin = (struct sockaddr_in *)&ifr->ifr_addr;
 387                 ifp->mask = sin->sin_addr;
 388                 hecp = make_encode(DSYM_STANDARD, CD_SUBNETMASK,
 389                     sizeof (struct in_addr), &ifp->mask, ENC_COPY);
 390                 replace_encode(&ifp->ecp, hecp, ENC_DONT_COPY);
 391 
 392                 /* Address */
 393                 if (ioctl(ip, SIOCGIFADDR, ifr) < 0) {
 394                         dhcpmsg(LOG_ERR, "Error encountered getting \
 395 interface: %s address: %s\n", ifp->nm,  strerror(errno));
 396                         free_encode_list(ifp->ecp);
 397                         free(ifp);
 398                         num_interfaces--;
 399                         continue;
 400                 }
 401                 /* LINTED [alignment ok] */
 402                 sin = (struct sockaddr_in *)&ifr->ifr_addr;
 403                 ifp->addr = sin->sin_addr;
 404 
 405                 /* MTU */
 406                 if (ioctl(ip, SIOCGIFMTU, ifr) < 0) {
 407                         dhcpmsg(LOG_ERR, "Error encountered getting \
 408 interface: %s MTU: %s\n", ifp->nm, strerror(errno));
 409                         free_encode_list(ifp->ecp);
 410                         free(ifp);
 411                         num_interfaces--;
 412                         continue;
 413                 }
 414 
 415                 ifp->mtu = ifr->ifr_metric;
 416                 mtu_tmp = htons(ifp->mtu);
 417                 hecp = make_encode(DSYM_STANDARD, CD_MTU, 2,
 418                     &mtu_tmp, ENC_COPY);
 419                 replace_encode(&ifp->ecp, hecp, ENC_DONT_COPY);
 420 
 421                 /* Attach to interface list */
 422                 if (!if_tail) {
 423                         (void) mutex_init(&if_head_mtx, USYNC_THREAD, 0);
 424                         (void) mutex_lock(&if_head_mtx);
 425                         if_tail = if_head = ifp;
 426                         (void) mutex_unlock(&if_head_mtx);
 427                 } else {
 428                         (void) mutex_lock(&if_head_mtx);
 429                         if_tail->next = ifp;
 430                         if_tail = ifp;
 431                         (void) mutex_unlock(&if_head_mtx);
 432                 }
 433         }
 434 
 435         free(reqbuf);
 436         free(user_if);
 437         (void) close(ip);
 438 
 439         if (if_head == NULL) {
 440                 num_interfaces = 0;
 441                 dhcpmsg(LOG_ERR, "Cannot find any valid interfaces.\n");
 442                 (void) mutex_destroy(&if_head_mtx);
 443                 return (EINVAL);
 444         }
 445         return (0);
 446 }
 447 
 448 /*
 449  * Destroy an *uninitialized* IF structure - returns next ifp.
 450  */
 451 static IF *
 452 zap_ifp(IF **ifp_prevpp, IF *ifp)
 453 {
 454         IF      *tifp;
 455 
 456         assert(MUTEX_HELD(&if_head_mtx));
 457 
 458         if (*ifp_prevpp == ifp) {
 459                 if_head = ifp->next;
 460                 *ifp_prevpp = if_head;
 461         } else
 462                 (*ifp_prevpp)->next = ifp->next;
 463 
 464         tifp = ifp->next;
 465 
 466         free(ifp);
 467 
 468         return (tifp);
 469 }
 470 
 471 /*
 472  * Monitor thread function. Poll on interface descriptors. Add valid BOOTP
 473  * packets to interfaces PKT_LIST.
 474  *
 475  * Because the buffer will potentially contain the ip/udp headers, we flag
 476  * this by setting the 'offset' field to the length of the two headers so that
 477  * free_plp() can "do the right thing"
 478  *
 479  * Monitor the given interface. Signals are handled by sig_client thread.
 480  *
 481  * We make some attempt to deal with marginal interfaces as follows. We
 482  * keep track of system errors (errors) and protocol errors (ifp->errors).
 483  * If we encounter more than DHCP_MON_SYSERRS in DHCP_MON_ERRINTVL,
 484  * then the interface thread will put itself to sleep for DHCP_MON_SLEEP
 485  * minutes.
 486  *
 487  * MT SAFE
 488  */
 489 static void *
 490 monitor_interface(void *argp)
 491 {
 492         PKT_LIST                *plp, *tplp;
 493         IF                      *ifp = (IF *)argp;
 494         int                     errors, err, i;
 495         uint_t                  verify_len;
 496         struct pollfd           pfd[DSRVR_NUM_DESC];
 497         struct strbuf           data;
 498         char                    cbuf[DN_MAX_CID_LEN], ntoab[INET_ADDRSTRLEN];
 499         time_t                  err_interval;
 500         dn_rec_t                dn;
 501         dsvc_dnet_t             *pnd;
 502         dsvc_clnt_t             *pcd;
 503         struct in_addr          netaddr, subnetaddr;
 504         dsvc_pendclnt_t         *workp;
 505         int                     open_ret;
 506         dsvc_thr_t              *freep;
 507         thread_t                tid;
 508         boolean_t               existing_allocation;
 509 
 510         if (debug) {
 511                 dhcpmsg(LOG_DEBUG, "Monitor (%04d/%s) started...\n",
 512                     ifp->if_thread, ifp->nm);
 513         }
 514 
 515         if (verbose)
 516                 disp_if(ifp);
 517 
 518         pfd[DSRVR_LBCAST].fd = ifp->descs[DSRVR_LBCAST];
 519         pfd[DSRVR_LBCAST].events = POLLIN | POLLPRI;
 520         pfd[DSRVR_DBCAST].fd = ifp->descs[DSRVR_DBCAST];
 521         pfd[DSRVR_DBCAST].events = POLLIN | POLLPRI;
 522         pfd[DSRVR_UCAST].fd = ifp->descs[DSRVR_UCAST];
 523         pfd[DSRVR_UCAST].events = POLLIN | POLLPRI;
 524 
 525         err_interval = time(NULL) + DHCP_MON_ERRINTVL;
 526         errors = 0;
 527         while (time_to_go == 0) {
 528                 if (errors > DHCP_MON_SYSERRS) {
 529                         if (time(NULL) < err_interval) {
 530                                 dhcpmsg(LOG_WARNING,
 531 "Monitor (%04d/%s): Too many system errors (%d), pausing for %d minute(s)...\n",
 532                                     ifp->if_thread, ifp->nm, errors,
 533                                     DHCP_MON_SYSERRS);
 534                                 (void) sleep(DHCP_MON_SLEEP);
 535                                 err_interval = time(NULL) + DHCP_MON_ERRINTVL;
 536                         }
 537                         errors = 0;
 538                 }
 539                 pfd[DSRVR_LBCAST].revents = 0;
 540                 pfd[DSRVR_DBCAST].revents = 0;
 541                 pfd[DSRVR_UCAST].revents = 0;
 542                 if (poll(&pfd[0], (nfds_t)DSRVR_NUM_DESC, INFTIM) < 0) {
 543                         dhcpmsg(LOG_ERR,
 544                             "Monitor (%04d/%s) Polling error: (%s).\n",
 545                             ifp->if_thread, ifp->nm, strerror(errno));
 546                         errors++;
 547                         continue;
 548                 }
 549                 /*
 550                  * See if we are to exit. We can't be holding any locks...
 551                  */
 552                 (void) mutex_lock(&ifp->ifp_mtx);
 553                 if (ifp->thr_exit) {
 554                         if (debug) {
 555                                 dhcpmsg(LOG_DEBUG,
 556                                     "Monitor (%04d/%s): exiting.\n",
 557                                     ifp->if_thread, ifp->nm);
 558                         }
 559                         (void) mutex_unlock(&ifp->ifp_mtx);
 560                         break;
 561                 }
 562                 (void) mutex_unlock(&ifp->ifp_mtx);
 563 
 564                 /* examine each socket for packets in turn */
 565                 for (i = 0; i < DSRVR_NUM_DESC; i++) {
 566                         if (pfd[i].revents == 0)
 567                                 continue;
 568                         if (pfd[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
 569                                 dhcpmsg(LOG_ERR, "Network interface "
 570                                     "error on device: %s(%s)\n", ifp->nm,
 571                                     dsrvr_socktype(i));
 572                                 errors++;
 573                                 continue;
 574                         }
 575                         if (!(pfd[i].revents & (POLLIN | POLLRDNORM))) {
 576                                 dhcpmsg(LOG_INFO, "Unsupported event "
 577                                     "on device %s(%s): %d\n", ifp->nm,
 578                                     dsrvr_socktype(i),
 579                                     pfd[i].revents);
 580                                 errors++;
 581                                 continue;
 582                         }
 583                         data.buf = smalloc(ifp->mtu);
 584                         data.len = recv(ifp->descs[i], data.buf, ifp->mtu, 0);
 585                         if (data.len < 0) {
 586                                 dhcpmsg(LOG_ERR, "Error: %s receiving UDP "
 587                                     "datagrams on %s(%s)\n",
 588                                     strerror(errno), ifp->nm,
 589                                     dsrvr_socktype(i));
 590                                 free(data.buf);
 591                                 errors++;
 592                                 continue;
 593                         } else
 594                                 verify_len = data.len;
 595 
 596                         if (debug) {
 597                                 dhcpmsg(LOG_INFO,
 598                                     "Datagram received on network device: "
 599                                     "%s(%s)\n", ifp->nm, dsrvr_socktype(i));
 600                         }
 601 
 602                         (void) mutex_lock(&ifp->ifp_mtx);
 603                         ifp->received++;
 604                         (void) mutex_unlock(&ifp->ifp_mtx);
 605 
 606                         if (verify_len < BASE_PKT_SIZE) {
 607                                 if (verbose) {
 608                                         dhcpmsg(LOG_INFO, "Short packet %d < "
 609                                             "%d on %s(%s) ignored\n",
 610                                             verify_len, sizeof (PKT),
 611                                             ifp->nm, dsrvr_socktype(i));
 612                                 }
 613                                 free(data.buf);
 614                                 (void) mutex_lock(&ifp->ifp_mtx);
 615                                 ifp->errors++;
 616                                 (void) mutex_unlock(&ifp->ifp_mtx);
 617                                 continue;
 618                         }
 619 
 620                         plp = (PKT_LIST *)smalloc(sizeof (PKT_LIST));
 621                         plp->offset = 0;
 622                         plp->len = data.len;
 623                         /* LINTED [alignment ok] */
 624                         plp->pkt = (PKT *)data.buf;
 625 
 626                         if (plp->pkt->hops >= max_hops + 1) {
 627                                 if (verbose) {
 628                                         dhcpmsg(LOG_INFO, "%s(%s): Packet "
 629                                             "dropped: too many hops: %d\n",
 630                                             ifp->nm, dsrvr_socktype(i),
 631                                             plp->pkt->hops);
 632                                 }
 633                                 free_plp(plp);
 634                                 (void) mutex_lock(&ifp->ifp_mtx);
 635                                 ifp->errors++;
 636                                 (void) mutex_unlock(&ifp->ifp_mtx);
 637                                 continue;
 638                         }
 639 
 640                         /* validate hardware len */
 641                         if (plp->pkt->hlen > sizeof (plp->pkt->chaddr))
 642                                 plp->pkt->hlen = sizeof (plp->pkt->chaddr);
 643 
 644                         if (debug && plp->pkt->giaddr.s_addr != 0L &&
 645                             plp->pkt->giaddr.s_addr != ifp->addr.s_addr) {
 646                                 dhcpmsg(LOG_INFO, "%s(%s): Packet received "
 647                                     "from relay agent: %s\n", ifp->nm,
 648                                     dsrvr_socktype(i), inet_ntop(AF_INET,
 649                                     &plp->pkt->giaddr, ntoab, sizeof (ntoab)));
 650                         }
 651 
 652                         if (!server_mode) {
 653                                 /*
 654                                  * Relay agent mode. No further processing
 655                                  * required ; we'll handle it here.
 656                                  */
 657                                 (void) mutex_lock(&if_head_mtx);
 658                                 err = relay_agent(ifp, plp);
 659                                 (void) mutex_unlock(&if_head_mtx);
 660                                 if (err != 0) {
 661                                         dhcpmsg(LOG_ERR, "Relay agent mode "
 662                                             "failed: %d (%s) on: %s(%s)\n",
 663                                             err, (plp->pkt->op == BOOTREPLY) ?
 664                                             "reply" : "request",  ifp->nm,
 665                                             dsrvr_socktype(i));
 666                                         errors++; /* considered system error */
 667                                 } else {
 668                                         /* update statistics */
 669                                         (void) mutex_lock(&ifp->ifp_mtx);
 670                                         ifp->processed++;
 671                                         ifp->received++;
 672                                         (void) mutex_unlock(&ifp->ifp_mtx);
 673                                 }
 674                                 free_plp(plp);
 675                                 continue;
 676                         }
 677 
 678 /* ============ Packets destined for bootp and dhcp server modules ========== */
 679 
 680                         /*
 681                          * Allow packets without RFC1048 magic cookies.
 682                          * Just don't do an options scan on them,
 683                          * thus we treat them as plain BOOTP packets.
 684                          * The BOOTP server can deal with requests of
 685                          * this type.
 686                          */
 687                         if (memcmp(plp->pkt->cookie, magic_cookie,
 688                             sizeof (magic_cookie)) != 0) {
 689                                 if (verbose) {
 690                                         dhcpmsg(LOG_INFO, "%s(%s): Client: %s "
 691                                             "using non-RFC1048 BOOTP cookie.\n",
 692                                             ifp->nm, dsrvr_socktype(i),
 693                                             disp_cid(plp, cbuf, sizeof (cbuf)));
 694                                 }
 695                                 plp->rfc1048 = B_FALSE;
 696                         } else {
 697                                 /*
 698                                  * Scan the options in the packet and fill in
 699                                  * the opts and vs fields in the * clientlist
 700                                  * structure.  If there's a DHCP message type
 701                                  * in the packet then it's a DHCP packet;
 702                                  * otherwise it's a BOOTP packet. Standard
 703                                  * options are RFC1048 style.
 704                                  */
 705                                 if (dhcp_options_scan(plp, B_FALSE) != 0) {
 706                                         dhcpmsg(LOG_ERR, "Garbled DHCP/BOOTP "
 707                                             "packet received on: %s(%s)\n",
 708                                             ifp->nm, dsrvr_socktype(i));
 709                                         free_plp(plp);
 710                                         (void) mutex_lock(&ifp->ifp_mtx);
 711                                         ifp->errors++;
 712                                         (void) mutex_unlock(&ifp->ifp_mtx);
 713                                         continue;
 714                                 }
 715                                 plp->rfc1048 = B_TRUE;
 716                         }
 717 
 718                         /*
 719                          * Link the new packet to the list of packets
 720                          * for this network/client. No need to lock plp,
 721                          * since it isn't visible outside this function yet.
 722                          */
 723                         if (plp->pkt->op != BOOTREQUEST) {
 724                                 dhcpmsg(LOG_ERR, "Unexpected packet received "
 725                                     "on %s(%s), BOOTP server port. Ignored.\n",
 726                                     ifp->nm, dsrvr_socktype(i));
 727                                 free_plp(plp);
 728                                 (void) mutex_lock(&ifp->ifp_mtx);
 729                                 ifp->errors++;
 730                                 (void) mutex_unlock(&ifp->ifp_mtx);
 731                                 continue;
 732                         }
 733 
 734                         determine_network(ifp, plp, &netaddr, &subnetaddr);
 735                         if ((err = open_dnet(&pnd, &netaddr, &subnetaddr)) !=
 736                             DSVC_SUCCESS) {
 737                                 if (verbose && err == DSVC_NO_TABLE) {
 738                                         netaddr.s_addr &= subnetaddr.s_addr;
 739                                         dhcpmsg(LOG_INFO, "%s(%s): There is no "
 740                                             "%s dhcp-network table for DHCP "
 741                                             "client's network.\n", ifp->nm,
 742                                             dsrvr_socktype(i),
 743                                             inet_ntop(AF_INET, &netaddr,
 744                                             ntoab, sizeof (ntoab)));
 745                                 }
 746                                 free_plp(plp);
 747                                 continue;
 748                         }
 749 
 750                         /* Find client */
 751                         get_clnt_id(plp, (uchar_t *)dn.dn_cid,
 752                             sizeof (dn.dn_cid), &dn.dn_cid_len);
 753                         open_ret = open_clnt(pnd, &pcd, dn.dn_cid,
 754                             dn.dn_cid_len, B_FALSE);
 755 
 756                         if (pcd == NULL) {
 757                                 free_plp(plp);
 758                                 close_dnet(pnd, B_FALSE);
 759                                 continue;
 760                         }
 761 
 762                         /*
 763                          * DOS via Packet flooding: ensure that each client's
 764                          * PKT_LIST never exceeds DHCP_MON_THRESHOLD pkts in
 765                          * length. If it does, we prune it from the head of
 766                          * the list, dropping sequential packets. Note that
 767                          * since DHCP is a multi-transaction protocol, we would
 768                          * like to be sure not to discard a REQUEST for an OFFER
 769                          * we've extended.
 770                          *
 771                          * TODO: we are still vulnerable to flooding attacks
 772                          * where bogus client ids are presented. This can be
 773                          * manually controlled via the MAX_CLIENTS and
 774                          * MAX_THREADS config file knobs.
 775                          */
 776                         (void) mutex_lock(&pcd->pkt_mtx);
 777                         if (pcd->pending > DHCP_MON_THRESHOLD) {
 778                                 if ((tplp = pcd->pkthead) != NULL) {
 779                                         detach_plp(pcd, tplp);
 780                                         free_plp(tplp);
 781                                         pcd->pending--;
 782                                 }
 783                         }
 784 
 785                         if (pcd->pkthead == NULL)
 786                                 pcd->pkthead = plp;
 787                         else {
 788                                 pcd->pkttail->next = plp;
 789                                 plp->prev = pcd->pkttail;
 790                         }
 791                         pcd->pkttail = plp;
 792                         pcd->pending++;
 793                         (void) mutex_unlock(&pcd->pkt_mtx);
 794 
 795                         /*
 796                          * Manage worker threads and deferred thread work list.
 797                          */
 798                         (void) mutex_lock(&pcd->pcd_mtx);
 799                         pcd->ifp = ifp;
 800                         if (pcd->clnt_thread == NULL &&
 801                             (pcd->flags & DHCP_PCD_CLOSING) == 0) {
 802                                 existing_allocation = B_FALSE;
 803                                 (void) mutex_lock(&pnd->thr_mtx);
 804                                 if ((freep = pnd->thrhead) != NULL) {
 805                                         existing_allocation = B_TRUE;
 806                                         /*
 807                                          * Restart a suspended thread.
 808                                          */
 809                                         pnd->thrhead = freep->thr_next;
 810                                         if (pnd->thrhead == NULL)
 811                                                 pnd->thrtail = NULL;
 812                                         (void) mutex_unlock(&pnd->thr_mtx);
 813 
 814                                         (void) mutex_lock(&freep->thr_mtx);
 815                                         freep->thr_flags &= ~DHCP_THR_LIST;
 816                                         freep->thr_next = NULL;
 817                                         freep->thr_pcd = pcd;
 818                                         (void) mutex_unlock(&freep->thr_mtx);
 819                                         pcd->clnt_thread = freep;
 820                                 } else if (max_threads != -1 &&
 821                                     pnd->nthreads >= max_threads) {
 822                                         /*
 823                                          * Add client once to deferred work
 824                                          * list, to keep track of future work.
 825                                          */
 826                                         if ((pcd->flags & DHCP_PCD_WORK) == 0) {
 827                                                 pcd->flags |= DHCP_PCD_WORK;
 828                                                 workp = (dsvc_pendclnt_t *)
 829                                                     smalloc(
 830                                                     sizeof (dsvc_pendclnt_t));
 831                                                 get_clnt_id(plp,
 832                                                     (uchar_t *)workp->pnd_cid,
 833                                                     sizeof (workp->pnd_cid),
 834                                                     &workp->pnd_cid_len);
 835                                                 if (pnd->workhead == NULL)
 836                                                         pnd->workhead = workp;
 837                                                 else {
 838                                                         pnd->worktail->
 839                                                             pnd_next = workp;
 840                                                 }
 841                                                 pnd->worktail = workp;
 842                                         }
 843                                         (void) mutex_unlock(&pnd->thr_mtx);
 844                                         (void) mutex_unlock(&pcd->pcd_mtx);
 845                                         if (open_ret == DSVC_SUCCESS)
 846                                                 close_clnt(pcd, B_FALSE);
 847                                         close_dnet(pnd, B_FALSE);
 848                                         continue;
 849                                 }
 850                                 if (pcd->clnt_thread == NULL) {
 851                                         pnd->nthreads++;
 852                                         (void) mutex_unlock(&pnd->thr_mtx);
 853                                         freep = pcd->clnt_thread =
 854                                             (dsvc_thr_t *)
 855                                             smalloc(sizeof (dsvc_thr_t));
 856                                         (void) mutex_init(&freep->thr_mtx,
 857                                             USYNC_THREAD, 0);
 858                                         freep->thr_pcd = pcd;
 859 
 860                                         /* Fire up a client thread. */
 861                                         if (thr_create(NULL, 0, monitor_client,
 862                                             freep, THR_BOUND | THR_SUSPENDED |
 863                                             THR_DETACHED, &freep->thr_tid) !=
 864                                             0) {
 865                                                 dhcpmsg(LOG_ERR, "%s(%s): "
 866                                                     "Error %s starting client "
 867                                                     "monitor thread.\n",
 868                                                     ifp->nm, dsrvr_socktype(i),
 869                                                     strerror(errno));
 870                                                 (void) mutex_lock(
 871                                                     &pnd->thr_mtx);
 872                                                 pnd->nthreads--;
 873                                                 (void) mutex_unlock(
 874                                                     &pnd->thr_mtx);
 875                                                 free(freep);
 876                                                 freep = pcd->clnt_thread = NULL;
 877                                         }
 878                                 }
 879                                 if (freep != NULL) {
 880                                         /*
 881                                          * Continue the new or reused thread.
 882                                          * Let it close the client.
 883                                          */
 884                                         open_ret = DSVC_BUSY;
 885                                         tid = freep->thr_tid;
 886                                         (void) mutex_unlock(&pcd->pcd_mtx);
 887                                         pcd = NULL;
 888                                         if (existing_allocation) {
 889                                                 (void) cond_signal(
 890                                                     &freep->thr_cv);
 891                                         } else {
 892                                                 (void) thr_continue(tid);
 893                                         }
 894                                 }
 895                         }
 896                         if (pcd != NULL) {
 897                                 (void) mutex_unlock(&pcd->pcd_mtx);
 898                                 if (open_ret == DSVC_SUCCESS)
 899                                         close_clnt(pcd, B_FALSE);
 900                         }
 901                         close_dnet(pnd, B_FALSE);
 902                 }
 903         }
 904         return (NULL);
 905 }
 906 
 907 /*
 908  * close interface sockets
 909  */
 910 static void
 911 close_sockets(IF *ifp) {
 912         int     i;
 913 
 914         for (i = 0; i < DSRVR_NUM_DESC; i++) {
 915                 if (ifp->descs[i] == -1)
 916                         continue;
 917                 (void) close(ifp->descs[i]);
 918                 ifp->descs[i] = -1;
 919         }
 920 }
 921 
 922 /*
 923  * initialize interface sockets.
 924  *
 925  * Returns: 0 for success, -1 otherwise.
 926  */
 927 static int
 928 init_sockets(IF *ifp)
 929 {
 930         int                     i, soptbuf = 1;
 931         struct sockaddr_in      sin;
 932 
 933         sin.sin_family = AF_INET;
 934         sin.sin_port = htons((short)IPPORT_BOOTPS + port_offset);
 935 
 936         ifp->descs[DSRVR_LBCAST] = -1;
 937         ifp->descs[DSRVR_DBCAST] = -1;
 938         ifp->descs[DSRVR_UCAST] = -1;
 939 
 940         for (i = 0; i < DSRVR_NUM_DESC; i++) {
 941                 ifp->descs[i] = socket(AF_INET, SOCK_DGRAM, 0);
 942                 if (ifp->descs[i] < 0) {
 943                         dhcpmsg(LOG_ERR, "Error opening socket on %s(%s) for "
 944                             "receiving UDP datagrams: %s\n",
 945                             ifp->nm, dsrvr_socktype(i), strerror(errno));
 946                         return (-1);
 947                 }
 948 
 949                 if (setsockopt(ifp->descs[i], SOL_SOCKET, SO_REUSEADDR,
 950                     &soptbuf, (int)sizeof (soptbuf)) < 0) {
 951                         dhcpmsg(LOG_DEBUG, "Setting socket option on %s(%s) "
 952                             "to allow reuse on send descriptor failed: %s\n",
 953                             ifp->nm, dsrvr_socktype(i), strerror(errno));
 954                         close_sockets(ifp);
 955                         return (-1);
 956                 }
 957 
 958                 (void) setsockopt(ifp->descs[i], SOL_SOCKET, SO_RCVBUF,
 959                     &socksize, sizeof (socksize));
 960                 (void) setsockopt(ifp->descs[i], SOL_SOCKET, SO_SNDBUF,
 961                     &socksize, sizeof (socksize));
 962 
 963                 switch (i) {
 964                         case DSRVR_LBCAST:
 965                                 if (setsockopt(ifp->descs[i], IPPROTO_IP,
 966                                     IP_BOUND_IF, &ifp->ifceno,
 967                                     (int)sizeof (char *)) < 0) {
 968                                         dhcpmsg(LOG_ERR,
 969                                             "Bind to index failed on %s: %s\n",
 970                                             ifp->nm, strerror(errno));
 971                                         close_sockets(ifp);
 972                                         return (-1);
 973                                 }
 974                                 sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
 975                                 break;
 976                         case DSRVR_DBCAST:
 977                                 sin.sin_addr.s_addr =
 978                                     ifp->addr.s_addr & ifp->mask.s_addr;
 979                                 break;
 980                         case DSRVR_UCAST:
 981                                 /* We send out the unicast socket */
 982                                 if (setsockopt(ifp->descs[i], SOL_SOCKET,
 983                                     SO_BROADCAST, &soptbuf,
 984                                     (int)sizeof (soptbuf)) < 0) {
 985                                         dhcpmsg(LOG_ERR, "Setting socket "
 986                                             "option on %s to allow broadcast "
 987                                             "on send descriptor failed: %s\n",
 988                                             ifp->nm, strerror(errno));
 989                                         close_sockets(ifp);
 990                                         return (-1);
 991                                 }
 992                                 sin.sin_addr.s_addr = ifp->addr.s_addr;
 993                                 break;
 994                 }
 995                 if (bind(ifp->descs[i],
 996                     (struct sockaddr *)&sin, sizeof (sin)) < 0) {
 997                         dhcpmsg(LOG_ERR,
 998                             "Error binding to UDP socket on %s(%s): %s\n",
 999                             ifp->nm, dsrvr_socktype(i), strerror(errno));
1000                         close_sockets(ifp);
1001                         return (-1);
1002                 }
1003         }
1004         return (0);
1005 }
1006 
1007 /*
1008  * Based on the list generated by find_interfaces(), possibly modified by
1009  * user arguments, open a stream for each valid / requested interface.
1010  *
1011  * If:
1012  *
1013  *      1) Only one interface exists, open a standard bidirectional UDP
1014  *              socket. Note that this is different than if only ONE
1015  *              interface is requested (but more exist).
1016  *
1017  *      2) If more than one valid interface exists, then attach to the
1018  *              datalink layer, push on the packet filter and buffering
1019  *              modules, and wait for fragment 0 IP packets that contain
1020  *              UDP packets with port 67 (server port).
1021  *
1022  *      Comments:
1023  *              Using DLPI to identify the interface thru which BOOTP
1024  *              packets pass helps in providing the correct response.
1025  *              Note that I will open a socket for use in transmitting
1026  *              responses, suitably specifying the destination relay agent
1027  *              or host. Note that if I'm unicasting to the client (broadcast
1028  *              flag not set), that somehow I have to clue the IP layer about
1029  *              the client's hw address. The only way I can see doing this is
1030  *              making the appropriate ARP table entry.
1031  *
1032  *              The only remaining unknown is dealing with clients that
1033  *              require broadcasting, and multiple interfaces exist. I assume
1034  *              that if I specify the interface's source address when
1035  *              opening the socket, that a limited broadcast will be
1036  *              directed to the correct net, and only the correct net.
1037  *
1038  *      Returns: 0 for success, non-zero for failure.
1039  */
1040 int
1041 open_interfaces(void)
1042 {
1043         int             inum, err = 0;
1044         IF              *ifp, *ifp_prevp;
1045 
1046         /* Uncover list of valid, user-selected interfaces to monitor */
1047         if ((err = find_interfaces()) != 0)
1048                 return (err);
1049 
1050         (void) mutex_lock(&if_head_mtx);
1051 
1052         /*
1053          * Setup valid interfaces.
1054          */
1055         ifp = ifp_prevp = if_head;
1056         err = inum = 0;
1057         while (ifp != NULL) {
1058                 if (init_sockets(ifp) < 0) {
1059                         ifp = zap_ifp(&ifp_prevp, ifp);
1060                         num_interfaces--;
1061                         continue;
1062                 }
1063 
1064                 /* Accounting */
1065                 ifp->transmit = ifp->received = 0;
1066                 ifp->duplicate = ifp->dropped = 0;
1067                 ifp->processed = 0;
1068 
1069                 /* ifp structure lock */
1070                 (void) mutex_init(&ifp->ifp_mtx, USYNC_THREAD, 0);
1071                 ifp->thr_exit = 0;
1072 
1073                 /* fire up monitor thread */
1074                 if (thr_create(NULL, 0, monitor_interface, ifp,
1075                     THR_BOUND, &ifp->if_thread) != 0) {
1076                         dhcpmsg(LOG_ERR,
1077 "Interface: %s - Error %s starting monitor thread.\n", ifp->nm,
1078                             strerror(errno));
1079                         close_sockets(ifp);
1080                         (void) mutex_destroy(&ifp->ifp_mtx);
1081                         ifp = zap_ifp(&ifp_prevp, ifp);
1082                         num_interfaces--;
1083                         continue;
1084                 }
1085                 inum++;
1086                 ifp_prevp = ifp;
1087                 ifp = ifp->next;
1088         }
1089         (void) mutex_unlock(&if_head_mtx);
1090 
1091         /*
1092          * We must succeed in configuring at least one interface
1093          * to be considered successful.
1094          */
1095         if (num_interfaces == 0) {
1096                 err = EINVAL;
1097                 dhcpmsg(LOG_ERR, "Cannot configure any interfaces.\n");
1098         }
1099         return (err);
1100 }
1101 
1102 /*
1103  * Detach the referenced plp from the client list.
1104  */
1105 void
1106 detach_plp(dsvc_clnt_t *pcd, PKT_LIST *plp)
1107 {
1108         assert(MUTEX_HELD(&pcd->pkt_mtx));
1109 
1110         if (plp->prev == NULL) {
1111                 pcd->pkthead = plp->next;
1112                 if (pcd->pkthead != NULL)
1113                         pcd->pkthead->prev = NULL;
1114         } else
1115                 plp->prev->next = plp->next;
1116 
1117         if (plp->next != NULL)
1118                 plp->next->prev = plp->prev;
1119         else {
1120                 pcd->pkttail = plp->prev;
1121                 if (pcd->pkttail != NULL)
1122                         pcd->pkttail->next = NULL;
1123         }
1124         plp->prev = plp->next = NULL;
1125 }
1126 
1127 /*
1128  * Write a packet to an interface.
1129  *
1130  * Returns 0 on success otherwise non-zero.
1131  */
1132 int
1133 write_interface(IF *ifp, PKT *clientp, int len, struct sockaddr_in *to)
1134 {
1135         int err;
1136 
1137         to->sin_family = AF_INET;
1138 
1139         if ((err = sendto(ifp->descs[DSRVR_UCAST], clientp, len, 0,
1140             (struct sockaddr *)to, sizeof (struct sockaddr))) < 0) {
1141                 dhcpmsg(LOG_ERR, "SENDTO: %s.\n", strerror(errno));
1142                 return (err);
1143         }
1144 
1145         (void) mutex_lock(&ifp->ifp_mtx);
1146         ifp->transmit++;
1147         (void) mutex_unlock(&ifp->ifp_mtx);
1148 
1149         return (0);
1150 }
1151 
1152 /*
1153  * Pop any packet filters, buffering modules, close stream, free encode
1154  * list, terminate monitor thread, free ifp. Return ifp next ptr.
1155  */
1156 static IF *
1157 close_interface(IF *ifp)
1158 {
1159         int             err;
1160         IF              *tifp;
1161 
1162         assert(ifp != NULL);
1163 
1164         assert(MUTEX_HELD(&if_head_mtx));
1165 
1166         (void) mutex_lock(&ifp->ifp_mtx);
1167         ifp->thr_exit = 1;
1168 
1169         close_sockets(ifp);     /* thread will exit poll ... */
1170         (void) mutex_unlock(&ifp->ifp_mtx);
1171 
1172         /*
1173          * Wait for the thread to exit. We release the if_head_mtx
1174          * lock, since the monitor thread(s) need to acquire it to traverse
1175          * the list - and we don't want to deadlock. Once the monitor thread
1176          * notices the thr_exit flag, it'll be gone anyway. Note that if_head
1177          * is changing (in close_interfaces()). At this point, only monitor
1178          * threads that haven't been reaped could be walking the interface
1179          * list. They will "see" the change in if_head.
1180          */
1181         (void) mutex_unlock(&if_head_mtx);
1182         if ((err = thr_join(ifp->if_thread, NULL, NULL)) != 0) {
1183                 dhcpmsg(LOG_ERR,
1184                     "Error %d while waiting for monitor %d of %s\n",
1185                     err, ifp->if_thread, ifp->nm);
1186         }
1187         (void) mutex_lock(&if_head_mtx);
1188 
1189         /*
1190          * Note: clients and their associated packet lists are freed prior
1191          * to interfaces being closed.
1192          */
1193 
1194         /* free encode list */
1195         free_encode_list(ifp->ecp);
1196 
1197         /* display statistics */
1198         disp_if_stats(ifp);
1199 
1200         ifp->received = ifp->processed = 0;
1201 
1202         (void) mutex_unlock(&ifp->ifp_mtx);
1203         (void) mutex_destroy(&ifp->ifp_mtx);
1204         tifp = ifp->next;
1205         free(ifp);
1206         return (tifp);
1207 }
1208 
1209 /*
1210  * Close all interfaces, freeing up associated resources.
1211  * This should only be called from main() during final exit.
1212  */
1213 void
1214 close_interfaces(void)
1215 {
1216         (void) mutex_lock(&if_head_mtx);
1217         for (; if_head != NULL; if_head = close_interface(if_head)) {
1218                 if (verbose) {
1219                         dhcpmsg(LOG_INFO, "Closing interface: %s\n",
1220                             if_head->nm);
1221                 }
1222         }
1223         (void) mutex_unlock(&if_head_mtx);
1224         (void) mutex_destroy(&if_head_mtx);
1225 }
1226 
1227 /*
1228  * display IF info. Must be MT Safe - called from monitor threads.
1229  */
1230 static void
1231 disp_if(IF *ifp)
1232 {
1233         char ntoab[INET_ADDRSTRLEN];
1234 
1235         dhcpmsg(LOG_INFO, "Thread Id: %04d - Monitoring Interface: %s *****\n",
1236             ifp->if_thread, ifp->nm);
1237         dhcpmsg(LOG_INFO, "MTU: %d\tType: %s\n", ifp->mtu, "SOCKET");
1238         if ((ifp->flags & IFF_POINTOPOINT) == 0)
1239                 dhcpmsg(LOG_INFO, "Broadcast: %s\n",
1240                     inet_ntop(AF_INET, &ifp->bcast, ntoab, sizeof (ntoab)));
1241         dhcpmsg(LOG_INFO, "Netmask: %s\n",
1242             inet_ntop(AF_INET, &ifp->mask, ntoab, sizeof (ntoab)));
1243         dhcpmsg(LOG_INFO, "Address: %s\n",
1244             inet_ntop(AF_INET, &ifp->addr, ntoab, sizeof (ntoab)));
1245 }
1246 
1247 /*
1248  * Display IF statistics.
1249  */
1250 void
1251 disp_if_stats(IF *ifp)
1252 {
1253         dhcpmsg(LOG_INFO, "Interface statistics for: %s **************\n",
1254             ifp->nm);
1255 
1256         dhcpmsg(LOG_INFO, "Pending DHCP offers: %d\n", ifp->offers);
1257         dhcpmsg(LOG_INFO, "Total Packets Transmitted: %d\n", ifp->transmit);
1258         dhcpmsg(LOG_INFO, "Total Packets Received: %d\n", ifp->received);
1259         dhcpmsg(LOG_INFO, "Total Packet Duplicates: %d\n", ifp->duplicate);
1260         dhcpmsg(LOG_INFO, "Total Packets Dropped: %d\n", ifp->dropped);
1261         dhcpmsg(LOG_INFO, "Total Packets Processed: %d\n", ifp->processed);
1262         dhcpmsg(LOG_INFO, "Total Protocol Errors: %d\n", ifp->errors);
1263 }
1264 
1265 /*
1266  * Setup the arp cache so that IP address 'ia' will be temporarily
1267  * bound to hardware address 'ha' of length 'len'. 'ia' is expected in
1268  * network order.
1269  *
1270  * Returns: 0 if the arp entry was made, 1 otherwise.
1271  */
1272 int
1273 set_arp(IF *ifp, struct in_addr *ia, uchar_t *ha, int len, uchar_t flags)
1274 {
1275         struct sockaddr_in      *si;
1276         struct xarpreq          arpreq;
1277         int                     err = 0;
1278         char                    scratch[DHCP_SCRATCH];
1279         uint_t                  scratch_len;
1280         char                    ntoab[INET_ADDRSTRLEN];
1281 
1282         (void) memset((caddr_t)&arpreq, 0, sizeof (arpreq));
1283 
1284         arpreq.xarp_ha.sdl_family = AF_LINK;
1285 
1286         si = (struct sockaddr_in *)&arpreq.xarp_pa;
1287         si->sin_family = AF_INET;
1288         si->sin_addr = *ia;  /* struct copy */
1289 
1290         switch (flags) {
1291         case DHCP_ARP_ADD:
1292                 if (debug) {
1293                         scratch_len = sizeof (scratch);
1294                         if (octet_to_hexascii(ha, len, scratch,
1295                             &scratch_len) != 0) {
1296                                 dhcpmsg(LOG_DEBUG, "Cannot convert ARP \
1297 request to ASCII: %s: len: %d\n",
1298                                     inet_ntop(AF_INET, ia,
1299                                     ntoab, sizeof (ntoab)),
1300                                     len);
1301                         } else {
1302                                 dhcpmsg(LOG_DEBUG,
1303                                     "Adding ARP entry: %s == %s\n",
1304                                     inet_ntop(AF_INET, ia,
1305                                     ntoab, sizeof (ntoab)),
1306                                     scratch);
1307                         }
1308                 }
1309                 arpreq.xarp_flags = ATF_INUSE | ATF_COM;
1310                 (void) memcpy(LLADDR(&arpreq.xarp_ha), ha, len);
1311                 arpreq.xarp_ha.sdl_alen = len;
1312 
1313                 if (ioctl(ifp->descs[DSRVR_UCAST], SIOCSXARP, &arpreq) < 0) {
1314                         dhcpmsg(LOG_ERR,
1315                             "ADD: Cannot modify ARP table to add: %s\n",
1316                             inet_ntop(AF_INET, ia, ntoab, sizeof (ntoab)));
1317                         err = 1;
1318                 }
1319                 break;
1320         case DHCP_ARP_DEL:
1321                 /* give it a good effort, but don't worry... */
1322                 (void) ioctl(ifp->descs[DSRVR_UCAST], SIOCDXARP, &arpreq);
1323                 break;
1324         default:
1325                 err = 1;
1326                 break;
1327         }
1328 
1329         return (err);
1330 }
1331 
1332 /*
1333  * Address and send a BOOTP reply packet appropriately. Does right thing
1334  * based on BROADCAST flag. Also checks if giaddr field is set, and
1335  * WE are the relay agent...
1336  *
1337  * Returns: 0 for success, nonzero otherwise (fatal)
1338  */
1339 int
1340 send_reply(IF *ifp, PKT *pp, int len, struct in_addr *dstp)
1341 {
1342         int                     local = B_FALSE;
1343         struct sockaddr_in      to;
1344         struct in_addr          if_in, cl_in;
1345         char                    ntoab[INET_ADDRSTRLEN];
1346 
1347         if (pp->giaddr.s_addr != 0L && ifp->addr.s_addr !=
1348             pp->giaddr.s_addr) {
1349                 /* Going thru a relay agent */
1350                 to.sin_addr.s_addr = pp->giaddr.s_addr;
1351                 to.sin_port = htons(IPPORT_BOOTPS + port_offset);
1352         } else {
1353                 to.sin_port = htons(IPPORT_BOOTPC + port_offset);
1354 
1355                 if (ntohs(pp->flags) & BCAST_MASK) {
1356                         /*
1357                          * TODO - what should we do if broadcast
1358                          * flag is set, but ptp connection?
1359                          */
1360                         if (debug)
1361                                 dhcpmsg(LOG_INFO,
1362                                     "Sending datagram to broadcast address.\n");
1363                         to.sin_addr.s_addr = INADDR_BROADCAST;
1364                 } else {
1365                         /*
1366                          * By default, we assume unicast!
1367                          */
1368                         to.sin_addr.s_addr = dstp->s_addr;
1369 
1370                         if (debug) {
1371                                 dhcpmsg(LOG_INFO,
1372                                     "Unicasting datagram to %s address.\n",
1373                                     inet_ntop(AF_INET, dstp,
1374                                     ntoab, sizeof (ntoab)));
1375                         }
1376                         if (ifp->addr.s_addr == pp->giaddr.s_addr) {
1377                                 /*
1378                                  * No doubt a reply packet which we, as
1379                                  * the relay agent, are supposed to deliver.
1380                                  * Local Delivery!
1381                                  */
1382                                 local = B_TRUE;
1383                         } else {
1384                                 /*
1385                                  * We can't use the giaddr field to
1386                                  * determine whether the client is local
1387                                  * or remote. Use the client's address,
1388                                  * our interface's address,  and our
1389                                  * interface's netmask to make this
1390                                  * determination.
1391                                  */
1392                                 if_in.s_addr = ntohl(ifp->addr.s_addr);
1393                                 if_in.s_addr &= ntohl(ifp->mask.s_addr);
1394                                 cl_in.s_addr = ntohl(dstp->s_addr);
1395                                 cl_in.s_addr &= ntohl(ifp->mask.s_addr);
1396                                 if (if_in.s_addr == cl_in.s_addr)
1397                                         local = B_TRUE;
1398                         }
1399 
1400                         if (local) {
1401                                 /*
1402                                  * Local delivery. If we can make an
1403                                  * ARP entry we'll unicast. But only in
1404                                  * cases when we do have the chaddr handy.
1405                                  * RFC2855 and IPoIB are cases that do not
1406                                  * send chaddr and set hlen = 0. Identify
1407                                  * such media by their htype, and rely on
1408                                  * in-kernel ARP for them.
1409                                  */
1410                                 if ((ifp->flags & IFF_NOARP) == 0 &&
1411                                     ((pp->htype == ARPHRD_IB) ||
1412                                     (set_arp(ifp, dstp, pp->chaddr, pp->hlen,
1413                                     DHCP_ARP_ADD) == 0))) {
1414                                         to.sin_addr.s_addr = dstp->s_addr;
1415                                 } else {
1416                                         to.sin_addr.s_addr = INADDR_BROADCAST;
1417                                 }
1418                         }
1419                 }
1420         }
1421         return (write_interface(ifp, pp, len, &to));
1422 }
1423 
1424 /*
1425  * Free pkts
1426  */
1427 void
1428 free_pktlist(dsvc_clnt_t *pcd)
1429 {
1430         PKT_LIST *plp, *plp_next;
1431         IF *ifp = pcd->ifp;
1432 
1433         assert(MUTEX_HELD(&pcd->pcd_mtx));
1434 
1435         plp = pcd->pkthead;
1436         while (plp != NULL) {
1437                 plp_next = plp;
1438                 plp = plp->next;
1439                 free_plp(plp_next);
1440                 ifp->dropped++;
1441                 pcd->pending--;
1442         }
1443         pcd->pkthead = NULL;
1444 }
1445 
1446 /* Check if address is one of the addresses we are listening on */
1447 boolean_t
1448 is_our_address(in_addr_t addr)
1449 {
1450         IF              *ifp;
1451         boolean_t       found = B_FALSE;
1452 
1453         (void) mutex_lock(&if_head_mtx);
1454         for (ifp = if_head; ifp != NULL; ifp = ifp->next) {
1455                 if (ifp->addr.s_addr == addr) {
1456                         found = B_TRUE;
1457                         break;
1458                 }
1459         }
1460         (void) mutex_unlock(&if_head_mtx);
1461         return (found);
1462 }