xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.lib/in.dhcpd/interfaces.c (revision 11466:d60272412fb0)
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 *
dsrvr_socktype(dsrvr_socktype_t stype)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
match_plp(PKT_LIST * alp,PKT_LIST * blp)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 *
refresh_pktlist(dsvc_clnt_t * pcd,PKT_LIST * plp)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
find_interfaces(void)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 *
zap_ifp(IF ** ifp_prevpp,IF * ifp)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 *
monitor_interface(void * argp)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
close_sockets(IF * ifp)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
init_sockets(IF * ifp)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
open_interfaces(void)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
detach_plp(dsvc_clnt_t * pcd,PKT_LIST * plp)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
write_interface(IF * ifp,PKT * clientp,int len,struct sockaddr_in * to)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 *
close_interface(IF * ifp)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
close_interfaces(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
disp_if(IF * ifp)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
disp_if_stats(IF * ifp)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
set_arp(IF * ifp,struct in_addr * ia,uchar_t * ha,int len,uchar_t flags)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
send_reply(IF * ifp,PKT * pp,int len,struct in_addr * dstp)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
free_pktlist(dsvc_clnt_t * pcd)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
is_our_address(in_addr_t addr)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 }
1463