summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.c')
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.c1445
1 files changed, 1445 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.c b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.c
new file mode 100644
index 0000000000..1c3333bf69
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.c
@@ -0,0 +1,1445 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 1993-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <stropts.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <netinet/dhcp.h>
+#include <dhcp_symbol.h>
+#include "dhcpd.h"
+#include "per_dnet.h"
+#include "interfaces.h"
+#include <v4_sum_impl.h>
+#include <locale.h>
+
+static int socksize = 64 * 1024; /* large socket window size for data */
+static const uchar_t magic_cookie[] = BOOTMAGIC;
+static void disp_if(IF *);
+
+/*
+ * Network interface configuration. This file contains routines which
+ * handle the input side of the DHCP/BOOTP/Relay agent. Multiple interfaces
+ * are handled by identifying explicitly each interface, and creating a
+ * stream for each. If only one usable interface exists, then a "normal"
+ * UDP socket is used for simplicity's sake.
+ */
+
+IF *if_head; /* head of interfaces list */
+mutex_t if_head_mtx; /* mutex for adding/deleting IF list entries */
+char *interfaces; /* user specified interfaces */
+static int num_interfaces; /* # of usable interfaces on the system */
+
+static char *
+dsrvr_socktype(dsrvr_socktype_t stype)
+{
+ char *rp;
+
+ switch (stype) {
+ case DSRVR_LBCAST:
+ rp = "limited broadcast";
+ break;
+ case DSRVR_DBCAST:
+ rp = "directed broadcast";
+ break;
+ case DSRVR_UCAST:
+ rp = "unicast";
+ break;
+ }
+ return (rp);
+}
+
+/*
+ * Given two packets, match them based on BOOTP header operation, packet len,
+ * hardware type, flags, ciaddr, DHCP type, client id, or chaddr.
+ * Returns B_TRUE if they match, B_FALSE otherwise.
+ */
+static boolean_t
+match_plp(PKT_LIST *alp, PKT_LIST *blp)
+{
+ DHCP_OPT *a, *b;
+
+ assert(alp != NULL && blp != NULL);
+
+ if (alp->pkt->op != blp->pkt->op ||
+ alp->len != alp->len ||
+ alp->pkt->htype != blp->pkt->htype ||
+ alp->pkt->flags != blp->pkt->flags ||
+ alp->pkt->ciaddr.s_addr != blp->pkt->ciaddr.s_addr)
+ return (B_FALSE); /* not even the same BOOTP type. */
+
+#ifdef DEBUG
+ if (alp->pkt->giaddr.s_addr != blp->pkt->giaddr.s_addr) {
+ dhcpmsg(LOG_DEBUG,
+ "%04d match_plp: giaddr mismatch on 0x%x, 0x%x\n",
+ thr_self());
+ }
+#endif /* DEBUG */
+
+ a = alp->opts[CD_DHCP_TYPE];
+ b = blp->opts[CD_DHCP_TYPE];
+ if (a == NULL && b == NULL) {
+ /* bootp */
+ if (memcmp(alp->pkt->chaddr, blp->pkt->chaddr,
+ alp->pkt->hlen) == 0)
+ return (B_TRUE);
+ } else if (a != NULL && b != NULL) {
+ if (a->value[0] == b->value[0]) {
+ /* dhcp - packet types match. */
+ a = alp->opts[CD_CLIENT_ID];
+ b = blp->opts[CD_CLIENT_ID];
+ if (a != NULL && b != NULL) {
+ if (memcmp(a->value, b->value, a->len) == 0)
+ return (B_TRUE);
+ } else {
+ if (memcmp(alp->pkt->chaddr, blp->pkt->chaddr,
+ alp->pkt->hlen) == 0)
+ return (B_TRUE);
+ }
+ }
+ }
+ return (B_FALSE);
+}
+
+/*
+ * Given a packet, searches for a later packet in the
+ * interface's client list. If the search is successful, the argument
+ * packet is deleted, and the later packet is returned with the appropriate
+ * fields/options modified.
+ *
+ * Matches are based on match_plp(). The list is scanned until the final packet
+ * which "matches" is found. The last match replaces
+ * the argument plp. Duplicates are deleted.
+ *
+ * General Notes: After the first candidate is found, the list is checked to
+ * the tail of the list for other matches. For each packet which is deleted.
+ * the duplicate statistic is incremented for each one. If no candidate is
+ * found, then the argument plp is returned.
+ *
+ * Caveats: What about length and contents of packets? By definition, a
+ * client is not supposed to be altering this between frames, so we should
+ * be ok. Since the argument plp may be destroyed, it is assumed to be
+ * detached.
+ */
+PKT_LIST *
+refresh_pktlist(dsvc_clnt_t *pcd, PKT_LIST *plp)
+{
+ PKT_LIST *wplp, *tplp, *retplp = NULL;
+ IF *ifp = pcd->ifp;
+
+ assert(_mutex_held(&pcd->pkt_mtx));
+
+ wplp = pcd->pkthead;
+ while (wplp != NULL) {
+ if (match_plp(plp, wplp)) {
+ pcd->pending--;
+
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->duplicate++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+
+ /*
+ * Note that tplp, retplp can be synonyms for
+ * wplp. The synonyms are used because moldy plp's
+ * will be nuked, and the plp to return will be
+ * detached.
+ */
+ tplp = wplp;
+ wplp = wplp->next;
+
+ if (retplp != NULL) {
+ /* moldy duplicates */
+ free_plp(retplp);
+ }
+ retplp = tplp;
+ detach_plp(pcd, retplp);
+ } else {
+ wplp = wplp->next;
+ }
+ }
+
+ if (retplp == NULL)
+ retplp = plp;
+ else {
+ if (debug) {
+ dhcpmsg(LOG_DEBUG,
+ "%04d: Refreshed (0x%x) to (0x%x)\n",
+ thr_self(), plp, retplp);
+ }
+ free_plp(plp);
+ }
+
+ return (retplp);
+}
+
+/*
+ * Queries the IP transport layer for configured interfaces. Those that
+ * are acceptable for use by our daemon have these characteristics:
+ *
+ * Not loopback
+ * Is UP
+ *
+ * Sets num_interfaces global to number of valid, selected interfaces.
+ *
+ * Returns: 0 for success, the appropriate errno on fatal failure.
+ *
+ * Notes: Code gleaned from the in.rarpd, solaris 2.2.
+ */
+static int
+find_interfaces(void)
+{
+ int i, k, ip, reqsize, numifs;
+ boolean_t found;
+ ushort_t mtu_tmp;
+ struct ifreq *reqbuf, *ifr;
+ struct ifconf ifconf;
+ IF *ifp, *if_tail;
+ struct sockaddr_in *sin;
+ char **user_if;
+ ENCODE *hecp;
+
+ if ((ip = open("/dev/ip", 0)) < 0) {
+ dhcpmsg(LOG_ERR, "Error: opening /dev/ip: %s\n",
+ strerror(errno));
+ return (1);
+ }
+
+ if (ioctl(ip, SIOCGIFNUM, &numifs) < 0) {
+ dhcpmsg(LOG_WARNING,
+ "Error discovering number of network interfaces: %s\n",
+ strerror(errno));
+ return (1);
+ }
+
+ reqsize = numifs * sizeof (struct ifreq);
+ reqbuf = (struct ifreq *)smalloc(reqsize);
+
+ ifconf.ifc_len = reqsize;
+ ifconf.ifc_buf = (caddr_t)reqbuf;
+
+ if (ioctl(ip, SIOCGIFCONF, &ifconf) < 0) {
+ dhcpmsg(LOG_ERR,
+ "Error getting network interface information: %s\n",
+ strerror(errno));
+ free(reqbuf);
+ (void) close(ip);
+ return (1);
+ }
+
+ /*
+ * Verify that user specified interfaces are valid.
+ */
+ user_if = (char **)smalloc(numifs * sizeof (char *));
+ if (interfaces != NULL) {
+ for (i = 0; i < numifs; i++) {
+ user_if[i] = strtok(interfaces, ",");
+ if (user_if[i] == NULL)
+ break; /* we're done */
+ interfaces = NULL; /* for next call to strtok() */
+
+ for (found = B_FALSE, ifr = ifconf.ifc_req;
+ ifr < &ifconf.ifc_req[ifconf.ifc_len /
+ sizeof (struct ifreq)]; ifr++) {
+ if (strcmp(user_if[i], ifr->ifr_name) == 0) {
+ found = B_TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ dhcpmsg(LOG_ERR,
+ "Invalid network interface: %s\n",
+ user_if[i]);
+ free(reqbuf);
+ free(user_if);
+ (void) close(ip);
+ return (1);
+ }
+ }
+ if (i < numifs)
+ user_if[i] = NULL;
+ } else
+ user_if[0] = NULL;
+
+ /*
+ * For each interface, build an interface structure. Ignore any
+ * LOOPBACK or down interfaces.
+ */
+ if_tail = if_head = NULL;
+ for (ifr = ifconf.ifc_req;
+ ifr < &ifconf.ifc_req[ifconf.ifc_len / sizeof (struct ifreq)];
+ ifr++) {
+ if (ioctl(ip, SIOCGIFFLAGS, ifr) < 0) {
+ dhcpmsg(LOG_ERR,
+"Error encountered getting interface: %s flags: %s\n",
+ ifr->ifr_name, strerror(errno));
+ continue;
+ }
+ if ((ifr->ifr_flags & IFF_LOOPBACK) ||
+ !(ifr->ifr_flags & IFF_UP))
+ continue;
+
+ num_interfaces++; /* all possible interfaces counted */
+
+ /*
+ * If the user specified a list of interfaces,
+ * we'll only consider the ones specified.
+ */
+ if (user_if[0] != NULL) {
+ for (i = 0; i < numifs; i++) {
+ if (user_if[i] == NULL)
+ break; /* skip this interface */
+ if (strcmp(user_if[i], ifr->ifr_name) == 0)
+ break; /* user wants this one */
+ }
+ if (i == numifs || user_if[i] == NULL)
+ continue; /* skip this interface */
+ } else if (strchr(ifr->ifr_name, ':') != NULL)
+ continue; /* skip virtual interfaces */
+
+ ifp = (IF *)smalloc(sizeof (IF));
+ (void) strcpy(ifp->nm, ifr->ifr_name);
+
+ ifp->ifceno = if_nametoindex(ifp->nm);
+ ifp->flags = ifr->ifr_flags;
+ for (k = 0; k < DSRVR_NUM_DESC; k++)
+ ifp->descs[k] = -1;
+
+ /*
+ * Broadcast address. Not valid for POINTOPOINT
+ * connections.
+ */
+ if ((ifp->flags & IFF_POINTOPOINT) == 0) {
+ if (ifp->flags & IFF_BROADCAST) {
+ if (ioctl(ip, SIOCGIFBRDADDR, ifr) < 0) {
+ dhcpmsg(LOG_ERR, "Error encountered \
+getting interface: %s broadcast address: %s\n", ifp->nm, strerror(errno));
+ free(ifp);
+ num_interfaces--;
+ continue;
+ }
+ /* LINTED [alignment ok] */
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ ifp->bcast = sin->sin_addr;
+ } else
+ ifp->bcast.s_addr = htonl(INADDR_ANY);
+
+ hecp = make_encode(DSYM_STANDARD, CD_BROADCASTADDR,
+ sizeof (struct in_addr), &ifp->bcast,
+ ENC_COPY);
+ replace_encode(&ifp->ecp, hecp, ENC_DONT_COPY);
+ }
+
+ /* Subnet mask */
+ if (ioctl(ip, SIOCGIFNETMASK, ifr) < 0) {
+ dhcpmsg(LOG_ERR, "Error encountered getting \
+interface: %s netmask: %s\n", ifp->nm, strerror(errno));
+ free_encode_list(ifp->ecp);
+ free(ifp);
+ num_interfaces--;
+ continue;
+ }
+ /* LINTED [alignment ok] */
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ ifp->mask = sin->sin_addr;
+ hecp = make_encode(DSYM_STANDARD, CD_SUBNETMASK,
+ sizeof (struct in_addr), &ifp->mask, ENC_COPY);
+ replace_encode(&ifp->ecp, hecp, ENC_DONT_COPY);
+
+ /* Address */
+ if (ioctl(ip, SIOCGIFADDR, ifr) < 0) {
+ dhcpmsg(LOG_ERR, "Error encountered getting \
+interface: %s address: %s\n", ifp->nm, strerror(errno));
+ free_encode_list(ifp->ecp);
+ free(ifp);
+ num_interfaces--;
+ continue;
+ }
+ /* LINTED [alignment ok] */
+ sin = (struct sockaddr_in *)&ifr->ifr_addr;
+ ifp->addr = sin->sin_addr;
+
+ /* MTU */
+ if (ioctl(ip, SIOCGIFMTU, ifr) < 0) {
+ dhcpmsg(LOG_ERR, "Error encountered getting \
+interface: %s MTU: %s\n", ifp->nm, strerror(errno));
+ free_encode_list(ifp->ecp);
+ free(ifp);
+ num_interfaces--;
+ continue;
+ }
+
+ ifp->mtu = ifr->ifr_metric;
+ mtu_tmp = htons(ifp->mtu);
+ hecp = make_encode(DSYM_STANDARD, CD_MTU, 2,
+ &mtu_tmp, ENC_COPY);
+ replace_encode(&ifp->ecp, hecp, ENC_DONT_COPY);
+
+ /* Attach to interface list */
+ if (!if_tail) {
+ (void) mutex_init(&if_head_mtx, USYNC_THREAD, 0);
+ (void) mutex_lock(&if_head_mtx);
+ if_tail = if_head = ifp;
+ (void) mutex_unlock(&if_head_mtx);
+ } else {
+ (void) mutex_lock(&if_head_mtx);
+ if_tail->next = ifp;
+ if_tail = ifp;
+ (void) mutex_unlock(&if_head_mtx);
+ }
+ }
+
+ free(reqbuf);
+ free(user_if);
+ (void) close(ip);
+
+ if (if_head == NULL) {
+ num_interfaces = 0;
+ dhcpmsg(LOG_ERR, "Cannot find any valid interfaces.\n");
+ (void) mutex_destroy(&if_head_mtx);
+ return (EINVAL);
+ }
+ return (0);
+}
+
+/*
+ * Destroy an *uninitialized* IF structure - returns next ifp.
+ */
+static IF *
+zap_ifp(IF **ifp_prevpp, IF *ifp)
+{
+ IF *tifp;
+
+ assert(_mutex_held(&if_head_mtx));
+
+ if (*ifp_prevpp == ifp) {
+ if_head = ifp->next;
+ *ifp_prevpp = if_head;
+ } else
+ (*ifp_prevpp)->next = ifp->next;
+
+ tifp = ifp->next;
+
+ free(ifp);
+
+ return (tifp);
+}
+
+/*
+ * Monitor thread function. Poll on interface descriptors. Add valid BOOTP
+ * packets to interfaces PKT_LIST.
+ *
+ * Because the buffer will potentially contain the ip/udp headers, we flag
+ * this by setting the 'offset' field to the length of the two headers so that
+ * free_plp() can "do the right thing"
+ *
+ * Monitor the given interface. Signals are handled by sig_client thread.
+ *
+ * We make some attempt to deal with marginal interfaces as follows. We
+ * keep track of system errors (errors) and protocol errors (ifp->errors).
+ * If we encounter more than DHCP_MON_SYSERRS in DHCP_MON_ERRINTVL,
+ * then the interface thread will put itself to sleep for DHCP_MON_SLEEP
+ * minutes.
+ *
+ * MT SAFE
+ */
+static void *
+monitor_interface(void *argp)
+{
+ PKT_LIST *plp, *tplp;
+ IF *ifp = (IF *)argp;
+ int errors, err, i;
+ uint_t verify_len;
+ struct pollfd pfd[DSRVR_NUM_DESC];
+ struct strbuf data;
+ char cbuf[DN_MAX_CID_LEN], ntoab[INET_ADDRSTRLEN];
+ time_t err_interval;
+ dn_rec_t dn;
+ dsvc_dnet_t *pnd;
+ dsvc_clnt_t *pcd;
+ struct in_addr netaddr, subnetaddr;
+ dsvc_pendclnt_t *workp;
+ int open_ret;
+ dsvc_thr_t *freep;
+ thread_t tid;
+ boolean_t existing_allocation;
+
+ if (debug) {
+ dhcpmsg(LOG_DEBUG, "Monitor (%04d/%s) started...\n",
+ ifp->if_thread, ifp->nm);
+ }
+
+ if (verbose)
+ disp_if(ifp);
+
+ pfd[DSRVR_LBCAST].fd = ifp->descs[DSRVR_LBCAST];
+ pfd[DSRVR_LBCAST].events = POLLIN | POLLPRI;
+ pfd[DSRVR_DBCAST].fd = ifp->descs[DSRVR_DBCAST];
+ pfd[DSRVR_DBCAST].events = POLLIN | POLLPRI;
+ pfd[DSRVR_UCAST].fd = ifp->descs[DSRVR_UCAST];
+ pfd[DSRVR_UCAST].events = POLLIN | POLLPRI;
+
+ err_interval = time(NULL) + DHCP_MON_ERRINTVL;
+ errors = 0;
+ while (time_to_go == 0) {
+ if (errors > DHCP_MON_SYSERRS) {
+ if (time(NULL) < err_interval) {
+ dhcpmsg(LOG_WARNING,
+"Monitor (%04d/%s): Too many system errors (%d), pausing for %d minute(s)...\n",
+ ifp->if_thread, ifp->nm, errors,
+ DHCP_MON_SYSERRS);
+ (void) sleep(DHCP_MON_SLEEP);
+ err_interval = time(NULL) + DHCP_MON_ERRINTVL;
+ }
+ errors = 0;
+ }
+ pfd[DSRVR_LBCAST].revents = 0;
+ pfd[DSRVR_DBCAST].revents = 0;
+ pfd[DSRVR_UCAST].revents = 0;
+ if (poll(&pfd[0], (nfds_t)DSRVR_NUM_DESC, INFTIM) < 0) {
+ dhcpmsg(LOG_ERR,
+ "Monitor (%04d/%s) Polling error: (%s).\n",
+ ifp->if_thread, ifp->nm, strerror(errno));
+ errors++;
+ continue;
+ }
+ /*
+ * See if we are to exit. We can't be holding any locks...
+ */
+ (void) mutex_lock(&ifp->ifp_mtx);
+ if (ifp->thr_exit) {
+ if (debug) {
+ dhcpmsg(LOG_DEBUG,
+ "Monitor (%04d/%s): exiting.\n",
+ ifp->if_thread, ifp->nm);
+ }
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ break;
+ }
+ (void) mutex_unlock(&ifp->ifp_mtx);
+
+ /* examine each socket for packets in turn */
+ for (i = 0; i < DSRVR_NUM_DESC; i++) {
+ if (pfd[i].revents == 0)
+ continue;
+ if (pfd[i].revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ dhcpmsg(LOG_ERR, "Network interface "
+ "error on device: %s(%s)\n", ifp->nm,
+ dsrvr_socktype(i));
+ errors++;
+ continue;
+ }
+ if (!(pfd[i].revents & (POLLIN | POLLRDNORM))) {
+ dhcpmsg(LOG_INFO, "Unsupported event "
+ "on device %s(%s): %d\n", ifp->nm,
+ dsrvr_socktype(i),
+ pfd[i].revents);
+ errors++;
+ continue;
+ }
+ data.buf = smalloc(ifp->mtu);
+ data.len = recv(ifp->descs[i], data.buf, ifp->mtu, 0);
+ if (data.len < 0) {
+ dhcpmsg(LOG_ERR, "Error: %s receiving UDP "
+ "datagrams on %s(%s)\n",
+ strerror(errno), ifp->nm,
+ dsrvr_socktype(i));
+ free(data.buf);
+ errors++;
+ continue;
+ } else
+ verify_len = data.len;
+
+ if (debug) {
+ dhcpmsg(LOG_INFO,
+ "Datagram received on network device: "
+ "%s(%s)\n", ifp->nm, dsrvr_socktype(i));
+ }
+
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->received++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+
+ if (verify_len < BASE_PKT_SIZE) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "Short packet %d < "
+ "%d on %s(%s) ignored\n",
+ verify_len, sizeof (PKT),
+ ifp->nm, dsrvr_socktype(i));
+ }
+ free(data.buf);
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->errors++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ continue;
+ }
+
+ plp = (PKT_LIST *)smalloc(sizeof (PKT_LIST));
+ plp->offset = 0;
+ plp->len = data.len;
+ /* LINTED [alignment ok] */
+ plp->pkt = (PKT *)data.buf;
+
+ if (plp->pkt->hops >= max_hops + 1) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "%s(%s): Packet "
+ "dropped: too many hops: %d\n",
+ ifp->nm, dsrvr_socktype(i),
+ plp->pkt->hops);
+ }
+ free_plp(plp);
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->errors++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ continue;
+ }
+
+ /* validate hardware len */
+ if (plp->pkt->hlen > sizeof (plp->pkt->chaddr))
+ plp->pkt->hlen = sizeof (plp->pkt->chaddr);
+
+ if (debug && plp->pkt->giaddr.s_addr != 0L &&
+ plp->pkt->giaddr.s_addr != ifp->addr.s_addr) {
+ dhcpmsg(LOG_INFO, "%s(%s): Packet received "
+ "from relay agent: %s\n", ifp->nm,
+ dsrvr_socktype(i), inet_ntop(AF_INET,
+ &plp->pkt->giaddr, ntoab, sizeof (ntoab)));
+ }
+
+ if (!server_mode) {
+ /*
+ * Relay agent mode. No further processing
+ * required ; we'll handle it here.
+ */
+ (void) mutex_lock(&if_head_mtx);
+ err = relay_agent(ifp, plp);
+ (void) mutex_unlock(&if_head_mtx);
+ if (err != 0) {
+ dhcpmsg(LOG_ERR, "Relay agent mode "
+ "failed: %d (%s) on: %s(%s)\n",
+ err, (plp->pkt->op == BOOTREPLY) ?
+ "reply" : "request", ifp->nm,
+ dsrvr_socktype(i));
+ errors++; /* considered system error */
+ } else {
+ /* update statistics */
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->processed++;
+ ifp->received++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ }
+ free_plp(plp);
+ continue;
+ }
+
+/* ============ Packets destined for bootp and dhcp server modules ========== */
+
+ /*
+ * Allow packets without RFC1048 magic cookies.
+ * Just don't do an options scan on them,
+ * thus we treat them as plain BOOTP packets.
+ * The BOOTP server can deal with requests of
+ * this type.
+ */
+ if (memcmp(plp->pkt->cookie, magic_cookie,
+ sizeof (magic_cookie)) != 0) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "%s(%s): Client: %s "
+ "using non-RFC1048 BOOTP cookie.\n",
+ ifp->nm, dsrvr_socktype(i),
+ disp_cid(plp, cbuf, sizeof (cbuf)));
+ }
+ plp->rfc1048 = B_FALSE;
+ } else {
+ /*
+ * Scan the options in the packet and fill in
+ * the opts and vs fields in the * clientlist
+ * structure. If there's a DHCP message type
+ * in the packet then it's a DHCP packet;
+ * otherwise it's a BOOTP packet. Standard
+ * options are RFC1048 style.
+ */
+ if (dhcp_options_scan(plp, B_FALSE) != 0) {
+ dhcpmsg(LOG_ERR, "Garbled DHCP/BOOTP "
+ "packet received on: %s(%s)\n",
+ ifp->nm, dsrvr_socktype(i));
+ free_plp(plp);
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->errors++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ continue;
+ }
+ plp->rfc1048 = B_TRUE;
+ }
+
+ /*
+ * Link the new packet to the list of packets
+ * for this network/client. No need to lock plp,
+ * since it isn't visible outside this function yet.
+ */
+ if (plp->pkt->op != BOOTREQUEST) {
+ dhcpmsg(LOG_ERR, "Unexpected packet received "
+ "on %s(%s), BOOTP server port. Ignored.\n",
+ ifp->nm, dsrvr_socktype(i));
+ free_plp(plp);
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->errors++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ continue;
+ }
+
+ determine_network(ifp, plp, &netaddr, &subnetaddr);
+ if ((err = open_dnet(&pnd, &netaddr, &subnetaddr)) !=
+ DSVC_SUCCESS) {
+ if (verbose && err == DSVC_NO_TABLE) {
+ netaddr.s_addr &= subnetaddr.s_addr;
+ dhcpmsg(LOG_INFO, "%s(%s): There is no "
+ "%s dhcp-network table for DHCP "
+ "client's network.\n", ifp->nm,
+ dsrvr_socktype(i),
+ inet_ntop(AF_INET, &netaddr,
+ ntoab, sizeof (ntoab)));
+ }
+ free_plp(plp);
+ continue;
+ }
+
+ /* Find client */
+ get_clnt_id(plp, (uchar_t *)dn.dn_cid,
+ sizeof (dn.dn_cid), &dn.dn_cid_len);
+ open_ret = open_clnt(pnd, &pcd, dn.dn_cid,
+ dn.dn_cid_len, B_FALSE);
+
+ if (pcd == NULL) {
+ free_plp(plp);
+ close_dnet(pnd, B_FALSE);
+ continue;
+ }
+
+ /*
+ * DOS via Packet flooding: ensure that each client's
+ * PKT_LIST never exceeds DHCP_MON_THRESHOLD pkts in
+ * length. If it does, we prune it from the head of
+ * the list, dropping sequential packets. Note that
+ * since DHCP is a multi-transaction protocol, we would
+ * like to be sure not to discard a REQUEST for an OFFER
+ * we've extended.
+ *
+ * TODO: we are still vulnerable to flooding attacks
+ * where bogus client ids are presented. This can be
+ * manually controlled via the MAX_CLIENTS and
+ * MAX_THREADS config file knobs.
+ */
+ (void) mutex_lock(&pcd->pkt_mtx);
+ if (pcd->pending > DHCP_MON_THRESHOLD) {
+ if ((tplp = pcd->pkthead) != NULL) {
+ detach_plp(pcd, tplp);
+ free_plp(tplp);
+ pcd->pending--;
+ }
+ }
+
+ if (pcd->pkthead == NULL)
+ pcd->pkthead = plp;
+ else {
+ pcd->pkttail->next = plp;
+ plp->prev = pcd->pkttail;
+ }
+ pcd->pkttail = plp;
+ pcd->pending++;
+ (void) mutex_unlock(&pcd->pkt_mtx);
+
+ /*
+ * Manage worker threads and deferred thread work list.
+ */
+ (void) mutex_lock(&pcd->pcd_mtx);
+ pcd->ifp = ifp;
+ if (pcd->clnt_thread == NULL &&
+ (pcd->flags & DHCP_PCD_CLOSING) == 0) {
+ existing_allocation = B_FALSE;
+ (void) mutex_lock(&pnd->thr_mtx);
+ if ((freep = pnd->thrhead) != NULL) {
+ existing_allocation = B_TRUE;
+ /*
+ * Restart a suspended thread.
+ */
+ pnd->thrhead = freep->thr_next;
+ if (pnd->thrhead == NULL)
+ pnd->thrtail = NULL;
+ (void) mutex_unlock(&pnd->thr_mtx);
+
+ (void) mutex_lock(&freep->thr_mtx);
+ freep->thr_flags &= ~DHCP_THR_LIST;
+ freep->thr_next = NULL;
+ freep->thr_pcd = pcd;
+ (void) mutex_unlock(&freep->thr_mtx);
+ pcd->clnt_thread = freep;
+ } else if (max_threads != -1 &&
+ pnd->nthreads >= max_threads) {
+ /*
+ * Add client once to deferred work
+ * list, to keep track of future work.
+ */
+ if ((pcd->flags & DHCP_PCD_WORK) == 0) {
+ pcd->flags |= DHCP_PCD_WORK;
+ workp = (dsvc_pendclnt_t *)
+ smalloc(
+ sizeof (dsvc_pendclnt_t));
+ get_clnt_id(plp,
+ (uchar_t *)workp->pnd_cid,
+ sizeof (workp->pnd_cid),
+ &workp->pnd_cid_len);
+ if (pnd->workhead == NULL)
+ pnd->workhead = workp;
+ else {
+ pnd->worktail->
+ pnd_next = workp;
+ }
+ pnd->worktail = workp;
+ }
+ (void) mutex_unlock(&pnd->thr_mtx);
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ if (open_ret == DSVC_SUCCESS)
+ close_clnt(pcd, B_FALSE);
+ close_dnet(pnd, B_FALSE);
+ continue;
+ }
+ if (pcd->clnt_thread == NULL) {
+ pnd->nthreads++;
+ (void) mutex_unlock(&pnd->thr_mtx);
+ freep = pcd->clnt_thread =
+ (dsvc_thr_t *)
+ smalloc(sizeof (dsvc_thr_t));
+ (void) mutex_init(&freep->thr_mtx,
+ USYNC_THREAD, 0);
+ freep->thr_pcd = pcd;
+
+ /* Fire up a client thread. */
+ if (thr_create(NULL, 0, monitor_client,
+ freep, THR_BOUND | THR_SUSPENDED |
+ THR_DETACHED, &freep->thr_tid) !=
+ 0) {
+ dhcpmsg(LOG_ERR, "%s(%s): "
+ "Error %s starting client "
+ "monitor thread.\n",
+ ifp->nm, dsrvr_socktype(i),
+ strerror(errno));
+ (void) mutex_lock(
+ &pnd->thr_mtx);
+ pnd->nthreads--;
+ (void) mutex_unlock(
+ &pnd->thr_mtx);
+ free(freep);
+ freep = pcd->clnt_thread = NULL;
+ }
+ }
+ if (freep != NULL) {
+ /*
+ * Continue the new or reused thread.
+ * Let it close the client.
+ */
+ open_ret = DSVC_BUSY;
+ tid = freep->thr_tid;
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ pcd = NULL;
+ if (existing_allocation) {
+ (void) cond_signal(
+ &freep->thr_cv);
+ } else {
+ (void) thr_continue(tid);
+ }
+ }
+ }
+ if (pcd != NULL) {
+ (void) mutex_unlock(&pcd->pcd_mtx);
+ if (open_ret == DSVC_SUCCESS)
+ close_clnt(pcd, B_FALSE);
+ }
+ close_dnet(pnd, B_FALSE);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * close interface sockets
+ */
+static void
+close_sockets(IF *ifp) {
+ int i;
+
+ for (i = 0; i < DSRVR_NUM_DESC; i++) {
+ if (ifp->descs[i] == -1)
+ continue;
+ (void) close(ifp->descs[i]);
+ ifp->descs[i] = -1;
+ }
+}
+
+/*
+ * initialize interface sockets.
+ *
+ * Returns: 0 for success, -1 otherwise.
+ */
+static int
+init_sockets(IF *ifp)
+{
+ int i, soptbuf = 1;
+ struct sockaddr_in sin;
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons((short)IPPORT_BOOTPS + port_offset);
+
+ ifp->descs[DSRVR_LBCAST] = -1;
+ ifp->descs[DSRVR_DBCAST] = -1;
+ ifp->descs[DSRVR_UCAST] = -1;
+
+ for (i = 0; i < DSRVR_NUM_DESC; i++) {
+ ifp->descs[i] = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ifp->descs[i] < 0) {
+ dhcpmsg(LOG_ERR, "Error opening socket on %s(%s) for "
+ "receiving UDP datagrams: %s\n",
+ ifp->nm, dsrvr_socktype(i), strerror(errno));
+ return (-1);
+ }
+
+ if (setsockopt(ifp->descs[i], SOL_SOCKET, SO_REUSEADDR,
+ &soptbuf, (int)sizeof (soptbuf)) < 0) {
+ dhcpmsg(LOG_DEBUG, "Setting socket option on %s(%s) "
+ "to allow reuse on send descriptor failed: %s\n",
+ ifp->nm, dsrvr_socktype(i), strerror(errno));
+ close_sockets(ifp);
+ return (-1);
+ }
+
+ (void) setsockopt(ifp->descs[i], SOL_SOCKET, SO_RCVBUF,
+ &socksize, sizeof (socksize));
+ (void) setsockopt(ifp->descs[i], SOL_SOCKET, SO_SNDBUF,
+ &socksize, sizeof (socksize));
+
+ switch (i) {
+ case DSRVR_LBCAST:
+ if (setsockopt(ifp->descs[i], IPPROTO_IP,
+ IP_BOUND_IF, &ifp->ifceno,
+ (int)sizeof (char *)) < 0) {
+ dhcpmsg(LOG_ERR,
+ "Bind to index failed on %s: %s\n",
+ ifp->nm, strerror(errno));
+ close_sockets(ifp);
+ return (-1);
+ }
+ sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
+ break;
+ case DSRVR_DBCAST:
+ sin.sin_addr.s_addr =
+ ifp->addr.s_addr & ifp->mask.s_addr;
+ break;
+ case DSRVR_UCAST:
+ /* We send out the unicast socket */
+ if (setsockopt(ifp->descs[i], SOL_SOCKET,
+ SO_BROADCAST, &soptbuf,
+ (int)sizeof (soptbuf)) < 0) {
+ dhcpmsg(LOG_ERR, "Setting socket "
+ "option on %s to allow broadcast "
+ "on send descriptor failed: %s\n",
+ ifp->nm, strerror(errno));
+ close_sockets(ifp);
+ return (-1);
+ }
+ sin.sin_addr.s_addr = ifp->addr.s_addr;
+ break;
+ }
+ if (bind(ifp->descs[i],
+ (struct sockaddr *)&sin, sizeof (sin)) < 0) {
+ dhcpmsg(LOG_ERR,
+ "Error binding to UDP socket on %s(%s): %s\n",
+ ifp->nm, dsrvr_socktype(i), strerror(errno));
+ close_sockets(ifp);
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Based on the list generated by find_interfaces(), possibly modified by
+ * user arguments, open a stream for each valid / requested interface.
+ *
+ * If:
+ *
+ * 1) Only one interface exists, open a standard bidirectional UDP
+ * socket. Note that this is different than if only ONE
+ * interface is requested (but more exist).
+ *
+ * 2) If more than one valid interface exists, then attach to the
+ * datalink layer, push on the packet filter and buffering
+ * modules, and wait for fragment 0 IP packets that contain
+ * UDP packets with port 67 (server port).
+ *
+ * Comments:
+ * Using DLPI to identify the interface thru which BOOTP
+ * packets pass helps in providing the correct response.
+ * Note that I will open a socket for use in transmitting
+ * responses, suitably specifying the destination relay agent
+ * or host. Note that if I'm unicasting to the client (broadcast
+ * flag not set), that somehow I have to clue the IP layer about
+ * the client's hw address. The only way I can see doing this is
+ * making the appropriate ARP table entry.
+ *
+ * The only remaining unknown is dealing with clients that
+ * require broadcasting, and multiple interfaces exist. I assume
+ * that if I specify the interface's source address when
+ * opening the socket, that a limited broadcast will be
+ * directed to the correct net, and only the correct net.
+ *
+ * Returns: 0 for success, non-zero for failure.
+ */
+int
+open_interfaces(void)
+{
+ int inum, err = 0;
+ IF *ifp, *ifp_prevp;
+
+ /* Uncover list of valid, user-selected interfaces to monitor */
+ if ((err = find_interfaces()) != 0)
+ return (err);
+
+ (void) mutex_lock(&if_head_mtx);
+
+ /*
+ * Setup valid interfaces.
+ */
+ ifp = ifp_prevp = if_head;
+ err = inum = 0;
+ while (ifp != NULL) {
+ if (init_sockets(ifp) < 0) {
+ ifp = zap_ifp(&ifp_prevp, ifp);
+ num_interfaces--;
+ continue;
+ }
+
+ /* Accounting */
+ ifp->transmit = ifp->received = 0;
+ ifp->duplicate = ifp->dropped = 0;
+ ifp->processed = 0;
+
+ /* ifp structure lock */
+ (void) mutex_init(&ifp->ifp_mtx, USYNC_THREAD, 0);
+ ifp->thr_exit = 0;
+
+ /* fire up monitor thread */
+ if (thr_create(NULL, 0, monitor_interface, ifp,
+ THR_BOUND, &ifp->if_thread) != 0) {
+ dhcpmsg(LOG_ERR,
+"Interface: %s - Error %s starting monitor thread.\n", ifp->nm,
+ strerror(errno));
+ close_sockets(ifp);
+ (void) mutex_destroy(&ifp->ifp_mtx);
+ ifp = zap_ifp(&ifp_prevp, ifp);
+ num_interfaces--;
+ continue;
+ }
+ inum++;
+ ifp_prevp = ifp;
+ ifp = ifp->next;
+ }
+ (void) mutex_unlock(&if_head_mtx);
+
+ /*
+ * We must succeed in configuring at least one interface
+ * to be considered successful.
+ */
+ if (num_interfaces == 0) {
+ err = EINVAL;
+ dhcpmsg(LOG_ERR, "Cannot configure any interfaces.\n");
+ }
+ return (err);
+}
+
+/*
+ * Detach the referenced plp from the client list.
+ */
+void
+detach_plp(dsvc_clnt_t *pcd, PKT_LIST *plp)
+{
+ assert(_mutex_held(&pcd->pkt_mtx));
+
+ if (plp->prev == NULL) {
+ pcd->pkthead = plp->next;
+ if (pcd->pkthead != NULL)
+ pcd->pkthead->prev = NULL;
+ } else
+ plp->prev->next = plp->next;
+
+ if (plp->next != NULL)
+ plp->next->prev = plp->prev;
+ else {
+ pcd->pkttail = plp->prev;
+ if (pcd->pkttail != NULL)
+ pcd->pkttail->next = NULL;
+ }
+ plp->prev = plp->next = NULL;
+}
+
+/*
+ * Write a packet to an interface.
+ *
+ * Returns 0 on success otherwise non-zero.
+ */
+int
+write_interface(IF *ifp, PKT *clientp, int len, struct sockaddr_in *to)
+{
+ int err;
+
+ to->sin_family = AF_INET;
+
+ if ((err = sendto(ifp->descs[DSRVR_UCAST], clientp, len, 0,
+ (struct sockaddr *)to, sizeof (struct sockaddr))) < 0) {
+ dhcpmsg(LOG_ERR, "SENDTO: %s.\n", strerror(errno));
+ return (err);
+ }
+
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->transmit++;
+ (void) mutex_unlock(&ifp->ifp_mtx);
+
+ return (0);
+}
+
+/*
+ * Pop any packet filters, buffering modules, close stream, free encode
+ * list, terminate monitor thread, free ifp. Return ifp next ptr.
+ */
+static IF *
+close_interface(IF *ifp)
+{
+ int err;
+ IF *tifp;
+
+ assert(ifp != NULL);
+
+ assert(_mutex_held(&if_head_mtx));
+
+ (void) mutex_lock(&ifp->ifp_mtx);
+ ifp->thr_exit = 1;
+
+ close_sockets(ifp); /* thread will exit poll ... */
+ (void) mutex_unlock(&ifp->ifp_mtx);
+
+ /*
+ * Wait for the thread to exit. We release the if_head_mtx
+ * lock, since the monitor thread(s) need to acquire it to traverse
+ * the list - and we don't want to deadlock. Once the monitor thread
+ * notices the thr_exit flag, it'll be gone anyway. Note that if_head
+ * is changing (in close_interfaces()). At this point, only monitor
+ * threads that haven't been reaped could be walking the interface
+ * list. They will "see" the change in if_head.
+ */
+ (void) mutex_unlock(&if_head_mtx);
+ if ((err = thr_join(ifp->if_thread, NULL, NULL)) != 0) {
+ dhcpmsg(LOG_ERR,
+ "Error %d while waiting for monitor %d of %s\n",
+ err, ifp->if_thread, ifp->nm);
+ }
+ (void) mutex_lock(&if_head_mtx);
+
+ /*
+ * Note: clients and their associated packet lists are freed prior
+ * to interfaces being closed.
+ */
+
+ /* free encode list */
+ free_encode_list(ifp->ecp);
+
+ /* display statistics */
+ disp_if_stats(ifp);
+
+ ifp->received = ifp->processed = 0;
+
+ (void) mutex_unlock(&ifp->ifp_mtx);
+ (void) mutex_destroy(&ifp->ifp_mtx);
+ tifp = ifp->next;
+ free(ifp);
+ return (tifp);
+}
+
+/*
+ * Close all interfaces, freeing up associated resources.
+ * This should only be called from main() during final exit.
+ */
+void
+close_interfaces(void)
+{
+ (void) mutex_lock(&if_head_mtx);
+ for (; if_head != NULL; if_head = close_interface(if_head)) {
+ if (verbose) {
+ dhcpmsg(LOG_INFO, "Closing interface: %s\n",
+ if_head->nm);
+ }
+ }
+ (void) mutex_unlock(&if_head_mtx);
+ (void) mutex_destroy(&if_head_mtx);
+}
+
+/*
+ * display IF info. Must be MT Safe - called from monitor threads.
+ */
+static void
+disp_if(IF *ifp)
+{
+ char ntoab[INET_ADDRSTRLEN];
+
+ dhcpmsg(LOG_INFO, "Thread Id: %04d - Monitoring Interface: %s *****\n",
+ ifp->if_thread, ifp->nm);
+ dhcpmsg(LOG_INFO, "MTU: %d\tType: %s\n", ifp->mtu, "SOCKET");
+ if ((ifp->flags & IFF_POINTOPOINT) == 0)
+ dhcpmsg(LOG_INFO, "Broadcast: %s\n",
+ inet_ntop(AF_INET, &ifp->bcast, ntoab, sizeof (ntoab)));
+ dhcpmsg(LOG_INFO, "Netmask: %s\n",
+ inet_ntop(AF_INET, &ifp->mask, ntoab, sizeof (ntoab)));
+ dhcpmsg(LOG_INFO, "Address: %s\n",
+ inet_ntop(AF_INET, &ifp->addr, ntoab, sizeof (ntoab)));
+}
+
+/*
+ * Display IF statistics.
+ */
+void
+disp_if_stats(IF *ifp)
+{
+ dhcpmsg(LOG_INFO, "Interface statistics for: %s **************\n",
+ ifp->nm);
+
+ dhcpmsg(LOG_INFO, "Pending DHCP offers: %d\n", ifp->offers);
+ dhcpmsg(LOG_INFO, "Total Packets Transmitted: %d\n", ifp->transmit);
+ dhcpmsg(LOG_INFO, "Total Packets Received: %d\n", ifp->received);
+ dhcpmsg(LOG_INFO, "Total Packet Duplicates: %d\n", ifp->duplicate);
+ dhcpmsg(LOG_INFO, "Total Packets Dropped: %d\n", ifp->dropped);
+ dhcpmsg(LOG_INFO, "Total Packets Processed: %d\n", ifp->processed);
+ dhcpmsg(LOG_INFO, "Total Protocol Errors: %d\n", ifp->errors);
+}
+
+/*
+ * Setup the arp cache so that IP address 'ia' will be temporarily
+ * bound to hardware address 'ha' of length 'len'. 'ia' is expected in
+ * network order.
+ *
+ * Returns: 0 if the arp entry was made, 1 otherwise.
+ */
+int
+set_arp(IF *ifp, struct in_addr *ia, uchar_t *ha, int len, uchar_t flags)
+{
+ struct sockaddr_in *si;
+ struct xarpreq arpreq;
+ int err = 0;
+ char scratch[DHCP_SCRATCH];
+ uint_t scratch_len;
+ char ntoab[INET_ADDRSTRLEN];
+
+ (void) memset((caddr_t)&arpreq, 0, sizeof (arpreq));
+
+ arpreq.xarp_ha.sdl_family = AF_LINK;
+
+ si = (struct sockaddr_in *)&arpreq.xarp_pa;
+ si->sin_family = AF_INET;
+ si->sin_addr = *ia; /* struct copy */
+
+ switch (flags) {
+ case DHCP_ARP_ADD:
+ if (debug) {
+ scratch_len = sizeof (scratch);
+ if (octet_to_hexascii(ha, len, scratch,
+ &scratch_len) != 0) {
+ dhcpmsg(LOG_DEBUG, "Cannot convert ARP \
+request to ASCII: %s: len: %d\n",
+ inet_ntop(AF_INET, ia,
+ ntoab, sizeof (ntoab)),
+ len);
+ } else {
+ dhcpmsg(LOG_DEBUG,
+ "Adding ARP entry: %s == %s\n",
+ inet_ntop(AF_INET, ia,
+ ntoab, sizeof (ntoab)),
+ scratch);
+ }
+ }
+ arpreq.xarp_flags = ATF_INUSE | ATF_COM;
+ (void) memcpy(LLADDR(&arpreq.xarp_ha), ha, len);
+ arpreq.xarp_ha.sdl_alen = len;
+
+ if (ioctl(ifp->descs[DSRVR_UCAST], SIOCSXARP, &arpreq) < 0) {
+ dhcpmsg(LOG_ERR,
+ "ADD: Cannot modify ARP table to add: %s\n",
+ inet_ntop(AF_INET, ia, ntoab, sizeof (ntoab)));
+ err = 1;
+ }
+ break;
+ case DHCP_ARP_DEL:
+ /* give it a good effort, but don't worry... */
+ (void) ioctl(ifp->descs[DSRVR_UCAST], SIOCDXARP, &arpreq);
+ break;
+ default:
+ err = 1;
+ break;
+ }
+
+ return (err);
+}
+
+/*
+ * Address and send a BOOTP reply packet appropriately. Does right thing
+ * based on BROADCAST flag. Also checks if giaddr field is set, and
+ * WE are the relay agent...
+ *
+ * Returns: 0 for success, nonzero otherwise (fatal)
+ */
+int
+send_reply(IF *ifp, PKT *pp, int len, struct in_addr *dstp)
+{
+ int local = B_FALSE;
+ struct sockaddr_in to;
+ struct in_addr if_in, cl_in;
+ char ntoab[INET_ADDRSTRLEN];
+
+ if (pp->giaddr.s_addr != 0L && ifp->addr.s_addr !=
+ pp->giaddr.s_addr) {
+ /* Going thru a relay agent */
+ to.sin_addr.s_addr = pp->giaddr.s_addr;
+ to.sin_port = htons(IPPORT_BOOTPS + port_offset);
+ } else {
+ to.sin_port = htons(IPPORT_BOOTPC + port_offset);
+
+ if (ntohs(pp->flags) & BCAST_MASK) {
+ /*
+ * TODO - what should we do if broadcast
+ * flag is set, but ptp connection?
+ */
+ if (debug)
+ dhcpmsg(LOG_INFO,
+ "Sending datagram to broadcast address.\n");
+ to.sin_addr.s_addr = INADDR_BROADCAST;
+ } else {
+ /*
+ * By default, we assume unicast!
+ */
+ to.sin_addr.s_addr = dstp->s_addr;
+
+ if (debug) {
+ dhcpmsg(LOG_INFO,
+ "Unicasting datagram to %s address.\n",
+ inet_ntop(AF_INET, dstp,
+ ntoab, sizeof (ntoab)));
+ }
+ if (ifp->addr.s_addr == pp->giaddr.s_addr) {
+ /*
+ * No doubt a reply packet which we, as
+ * the relay agent, are supposed to deliver.
+ * Local Delivery!
+ */
+ local = B_TRUE;
+ } else {
+ /*
+ * We can't use the giaddr field to
+ * determine whether the client is local
+ * or remote. Use the client's address,
+ * our interface's address, and our
+ * interface's netmask to make this
+ * determination.
+ */
+ if_in.s_addr = ntohl(ifp->addr.s_addr);
+ if_in.s_addr &= ntohl(ifp->mask.s_addr);
+ cl_in.s_addr = ntohl(dstp->s_addr);
+ cl_in.s_addr &= ntohl(ifp->mask.s_addr);
+ if (if_in.s_addr == cl_in.s_addr)
+ local = B_TRUE;
+ }
+
+ if (local) {
+ /*
+ * Local delivery. If we can make an
+ * ARP entry we'll unicast. But only in
+ * cases when we do have the chaddr handy.
+ * RFC2855 and IPoIB are cases that do not
+ * send chaddr and set hlen = 0. Identify
+ * such media by their htype, and rely on
+ * in-kernel ARP for them.
+ */
+ if ((ifp->flags & IFF_NOARP) == 0 &&
+ ((pp->htype == ARPHRD_IB) ||
+ (set_arp(ifp, dstp, pp->chaddr, pp->hlen,
+ DHCP_ARP_ADD) == 0))) {
+ to.sin_addr.s_addr = dstp->s_addr;
+ } else {
+ to.sin_addr.s_addr = INADDR_BROADCAST;
+ }
+ }
+ }
+ }
+ return (write_interface(ifp, pp, len, &to));
+}
+
+/*
+ * Free pkts
+ */
+void
+free_pktlist(dsvc_clnt_t *pcd)
+{
+ PKT_LIST *plp, *plp_next;
+ IF *ifp = pcd->ifp;
+
+ assert(_mutex_held(&pcd->pcd_mtx));
+
+ plp = pcd->pkthead;
+ while (plp != NULL) {
+ plp_next = plp;
+ plp = plp->next;
+ free_plp(plp_next);
+ ifp->dropped++;
+ pcd->pending--;
+ }
+ pcd->pkthead = NULL;
+}