xref: /illumos-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c (revision 5d0bc3ededb82d77f7c33d8f58e517a837ba5140)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <net/if.h>
31 #include <sys/dlpi.h>
32 #include <stdlib.h>
33 #include <sys/sockio.h>
34 #include <netinet/in.h>
35 #include <netinet/dhcp.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <netinet/if_ether.h>
39 #include <signal.h>
40 #include <dhcpmsg.h>
41 #include <libdevinfo.h>
42 
43 #include "interface.h"
44 #include "util.h"
45 #include "dlpi_io.h"
46 #include "packet.h"
47 #include "defaults.h"
48 #include "states.h"
49 #include "script_handler.h"
50 
51 /*
52  * note to the reader:
53  *
54  * the terminology in here is slightly confusing.  in particular, the
55  * term `ifslist' is used to refer both to the `struct ifslist' entry
56  * that makes up a specific interface entry, and the `internal
57  * ifslist' which is a linked list of struct ifslists.  to reduce
58  * confusion, in the comments, a `struct ifslist' is referred to as
59  * an `ifs', and `ifslist' refers to the internal ifslist.
60  *
61  */
62 
63 static struct ifslist	*ifsheadp;
64 static unsigned int	ifscount;
65 
66 static void	init_ifs(struct ifslist *);
67 static void	free_ifs(struct ifslist *);
68 static void	cancel_ifs_timer(struct ifslist *, int);
69 
70 static boolean_t	get_prom_prop(const char *, const char *, uchar_t **,
71 			    unsigned int *);
72 
73 /*
74  * insert_ifs(): creates a new ifs and chains it on the ifslist.  initializes
75  *		 state which remains consistent across all use of the ifs entry
76  *
77  *   input: const char *: the name of the ifs entry (interface name)
78  *	    boolean_t: if B_TRUE, we're adopting the interface
79  *	    int *: ignored on input; if insert_ifs fails, set to a DHCP_IPC_E_*
80  *		   error code with the reason why
81  *  output: struct ifslist *: a pointer to the new ifs entry, or NULL on failure
82  */
83 
84 struct ifslist *
85 insert_ifs(const char *if_name, boolean_t is_adopting, int *error)
86 {
87 	uint32_t		buf[DLPI_BUF_MAX / sizeof (uint32_t)];
88 	dl_info_ack_t		*dlia = (dl_info_ack_t *)buf;
89 	caddr_t			dl_addr;
90 	struct ifreq    	ifr;
91 	unsigned int		i, client_id_len = 0;
92 	uchar_t			*client_id = NULL;
93 	const char		*prl;
94 	struct ifslist		*ifsp;
95 	long			seed;
96 
97 	ifsp = lookup_ifs(if_name);
98 	if (ifsp != NULL) {
99 		*error = DHCP_IPC_E_INT;	/* should never happen */
100 		return (NULL);
101 	}
102 
103 	/*
104 	 * okay, we've got a request to put a new interface under our
105 	 * control.  it's our job to set everything that doesn't
106 	 * change for the life of the interface.  (state that changes
107 	 * should be initialized in init_ifs() and reset by reset_ifs())
108 	 *
109 	 *  1. verify the interface can support DHCP
110 	 *  2. get the interface mtu
111 	 *  3. get the interface hardware type and hardware length
112 	 *  4. get the interface hardware address
113 	 *  5. get the interface broadcast address
114 	 *  6. get the interface flags
115 	 */
116 
117 	ifsp = calloc(1, sizeof (struct ifslist));
118 	if (ifsp == NULL) {
119 		dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate ifs entry for "
120 		    "%s", if_name);
121 		*error = DHCP_IPC_E_MEMORY;
122 		return (NULL);
123 	}
124 
125 	(void) strlcpy(ifsp->if_name, if_name, IFNAMSIZ);
126 
127 	/* step 1 */
128 	ifsp->if_dlpi_fd = dlpi_open(if_name, dlia, sizeof (buf), ETHERTYPE_IP);
129 	if (ifsp->if_dlpi_fd == -1) {
130 		*error = DHCP_IPC_E_INVIF;
131 		goto failure;
132 	}
133 
134 	init_ifs(ifsp);			/* ifsp->if_dlpi_fd must be valid */
135 	ipc_action_init(ifsp);
136 
137 	/* step 2 */
138 	ifsp->if_max = dlia->dl_max_sdu;
139 	ifsp->if_opt = ifsp->if_max - BASE_PKT_SIZE;
140 	ifsp->if_min = dlia->dl_min_sdu;
141 
142 	if (ifsp->if_max < DHCP_DEF_MAX_SIZE) {
143 		dhcpmsg(MSG_ERROR, "insert_ifs: %s does not have a large "
144 		    "enough maximum SDU to support DHCP", if_name);
145 		*error = DHCP_IPC_E_INVIF;
146 		goto failure;
147 	}
148 
149 	/* step 3 */
150 	ifsp->if_hwtype = dlpi_to_arp(dlia->dl_mac_type);
151 	ifsp->if_hwlen  = dlia->dl_addr_length - abs(dlia->dl_sap_length);
152 
153 	dhcpmsg(MSG_DEBUG, "insert_ifs: %s: sdumax %d, optmax %d, hwtype %d, "
154 	    "hwlen %d", if_name, ifsp->if_max, ifsp->if_opt, ifsp->if_hwtype,
155 	    ifsp->if_hwlen);
156 
157 	/* step 4 */
158 	ifsp->if_hwaddr = malloc(ifsp->if_hwlen);
159 	if (ifsp->if_hwaddr == NULL) {
160 		dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate if_hwaddr "
161 		    "for %s", if_name);
162 		*error = DHCP_IPC_E_MEMORY;
163 		goto failure;
164 	}
165 
166 	/*
167 	 * depending on the DLPI device, the sap and hardware addresses
168 	 * can be in either order within the dlsap address; find the
169 	 * location of the hardware address using dl_sap_length.  see the
170 	 * DLPI specification for more on this braindamage.
171 	 */
172 
173 	dl_addr = (caddr_t)dlia + dlia->dl_addr_offset;
174 	if (dlia->dl_sap_length > 0) {
175 		ifsp->if_sap_before++;
176 		dl_addr += dlia->dl_sap_length;
177 	}
178 
179 	(void) memcpy(ifsp->if_hwaddr, dl_addr, ifsp->if_hwlen);
180 
181 	/* step 5 */
182 	ifsp->if_saplen = abs(dlia->dl_sap_length);
183 	ifsp->if_daddr  = build_broadcast_dest(dlia, &ifsp->if_dlen);
184 	if (ifsp->if_daddr == NULL) {
185 		dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate if_daddr "
186 		    "for %s", if_name);
187 		*error = DHCP_IPC_E_MEMORY;
188 		goto failure;
189 	}
190 
191 	/* step 6 */
192 	(void) strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
193 
194 	if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
195 		if (errno == ENXIO)
196 			*error = DHCP_IPC_E_INVIF;
197 		else
198 			*error = DHCP_IPC_E_INT;
199 		dhcpmsg(MSG_ERR, "insert_ifs: SIOCGIFFLAGS for %s", if_name);
200 		goto failure;
201 	}
202 
203 	/*
204 	 * if DHCPRUNNING is already set on the interface and we're
205 	 * not adopting it, the agent probably crashed and burned.
206 	 * note it, but don't let it stop the proceedings.  we're
207 	 * pretty sure we're not already running, since we wouldn't
208 	 * have been able to bind to our IPC port.
209 	 */
210 
211 	if ((is_adopting == B_FALSE) && (ifr.ifr_flags & IFF_DHCPRUNNING))
212 		dhcpmsg(MSG_WARNING, "insert_ifs: DHCP flag already set on %s",
213 		    if_name);
214 
215 	ifr.ifr_flags |= IFF_DHCPRUNNING;
216 	(void) ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr);
217 
218 	ifsp->if_send_pkt.pkt = calloc(ifsp->if_max, 1);
219 	if (ifsp->if_send_pkt.pkt == NULL) {
220 		dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate if_send_pkt "
221 		    "for %s", if_name);
222 		*error = DHCP_IPC_E_MEMORY;
223 		goto failure;
224 	}
225 
226 	if (is_adopting) {
227 		/*
228 		 * if the agent is adopting a lease OBP is initially
229 		 * searched for a client-id
230 		 */
231 
232 		dhcpmsg(MSG_DEBUG, "insert_ifs: getting /chosen:clientid "
233 		    "property");
234 
235 		if (!get_prom_prop("chosen", "client-id", &ifsp->if_cid,
236 		    &client_id_len)) {
237 			/*
238 			 * a failure occurred trying to acquire the client-id
239 			 */
240 
241 			dhcpmsg(MSG_DEBUG, "insert_ifs: cannot allocate client "
242 			    "id for %s", if_name);
243 			*error = DHCP_IPC_E_INT;
244 			goto failure;
245 		} else if (dlia->dl_mac_type == DL_IB && ifsp->if_cid == NULL) {
246 			/*
247 			 * when the interface is infiniband and the agent
248 			 * is adopting the lease there must be an OBP
249 			 * client-id.
250 			 */
251 
252 			dhcpmsg(MSG_DEBUG, "insert_ifs: no /chosen:clientid"
253 			    "id for %s", if_name);
254 			*error = DHCP_IPC_E_INT;
255 			goto failure;
256 		}
257 
258 		ifsp->if_cidlen = client_id_len;
259 	} else {
260 		/*
261 		 * look in defaults file for the client-id
262 		 */
263 
264 		dhcpmsg(MSG_DEBUG, "insert_ifs: getting defaults client-id "
265 		    "property");
266 
267 		client_id = df_get_octet(if_name, DF_CLIENT_ID, &client_id_len);
268 
269 		/*
270 		 * at this point, all logical interfaces must be explicitly
271 		 * configured with a client id by the administrator.
272 		 */
273 
274 		if (client_id == NULL && strchr(if_name, ':') != NULL) {
275 			dhcpmsg(MSG_ERROR, "no client id configured for "
276 			    "logical interface %s; cannot manage", if_name);
277 			*error = DHCP_IPC_E_NOIFCID;
278 			goto failure;
279 		}
280 
281 		if (client_id != NULL) {
282 			/*
283 			 * the defaults client-id value must be copied out to
284 			 * another buffer
285 			 */
286 
287 			ifsp->if_cid = calloc(client_id_len, sizeof (uchar_t));
288 
289 			if (ifsp->if_cid == NULL) {
290 				dhcpmsg(MSG_ERR, "insert_ifs: cannot "
291 				    "allocate client id for %s", if_name);
292 				*error = DHCP_IPC_E_MEMORY;
293 				goto failure;
294 			}
295 
296 			(void) memcpy(ifsp->if_cid, client_id, client_id_len);
297 
298 			ifsp->if_cidlen = client_id_len;
299 		} else if (dlia->dl_mac_type == DL_IB) {
300 			/*
301 			 * This comes from DHCP over IPoIB spec. In the absence
302 			 * of an user specified client id, IPoIB automatically
303 			 * uses the required format, with the unique 4 octet
304 			 * value set to 0 (since IPoIB driver allows only a
305 			 * single interface on a port with a specific GID to
306 			 * belong to an IP subnet (PSARC 2001/289,
307 			 * FWARC 2002/702).
308 			 *
309 			 *   Type  Client-Identifier
310 			 * +-----+-----+-----+-----+-----+----....----+
311 			 * |  0  |  0 (4 octets)   |   GID (16 octets)|
312 			 * +-----+-----+-----+-----+-----+----....----+
313 			 */
314 			ifsp->if_cidlen = 1 + 4 + 16;
315 			ifsp->if_cid = client_id = malloc(ifsp->if_cidlen);
316 			if (ifsp->if_cid == NULL) {
317 				dhcpmsg(MSG_ERR, "insert_ifs: cannot "
318 				    "allocate client id for %s", if_name);
319 				*error = DHCP_IPC_E_MEMORY;
320 				goto failure;
321 			}
322 
323 			/*
324 			 * Pick the GID from the mac address. The format
325 			 * of the hardware address is:
326 			 * +-----+-----+-----+-----+----....----+
327 			 * | QPN (4 octets)  |   GID (16 octets)|
328 			 * +-----+-----+-----+-----+----....----+
329 			 */
330 			(void) memcpy(client_id + 5, ifsp->if_hwaddr + 4,
331 			    ifsp->if_hwlen - 4);
332 			(void) memset(client_id, 0, 5);
333 		}
334 	}
335 
336 	/*
337 	 * initialize the parameter request list, if there is one.
338 	 */
339 
340 	prl = df_get_string(if_name, DF_PARAM_REQUEST_LIST);
341 	if (prl == NULL)
342 		ifsp->if_prl = NULL;
343 	else {
344 		for (ifsp->if_prllen = 1, i = 0; prl[i] != '\0'; i++)
345 			if (prl[i] == ',')
346 				ifsp->if_prllen++;
347 
348 		ifsp->if_prl = malloc(ifsp->if_prllen);
349 		if (ifsp->if_prl == NULL) {
350 			dhcpmsg(MSG_WARNING, "insert_ifs: cannot allocate "
351 			    "parameter request list for %s (continuing)",
352 			    if_name);
353 		} else {
354 			for (i = 0; i < ifsp->if_prllen; prl++, i++) {
355 				ifsp->if_prl[i] = strtoul(prl, NULL, 0);
356 				while (*prl != ',' && *prl != '\0')
357 					prl++;
358 				if (*prl == '\0')
359 					break;
360 			}
361 		}
362 	}
363 
364 	ifsp->if_offer_wait = df_get_int(if_name, DF_OFFER_WAIT);
365 
366 	/*
367 	 * we're past the point of failure; chain it on.
368 	 */
369 
370 	ifsp->next	= ifsheadp;
371 	ifsp->prev	= NULL;
372 	ifsheadp	= ifsp;
373 
374 	if (ifsheadp->next != NULL)
375 		ifsheadp->next->prev = ifsheadp;
376 
377 	hold_ifs(ifsp);
378 	ifscount++;
379 
380 	if (inactivity_id != -1) {
381 		if (iu_cancel_timer(tq, inactivity_id, NULL) == 1)
382 			inactivity_id = -1;
383 	}
384 
385 	/*
386 	 * seed the random number generator, since we're going to need it
387 	 * to set transaction id's and for exponential backoff.  if an
388 	 * interface is already initialized, then we just end up harmlessly
389 	 * reseeding it.  note that we try to spread the hardware address
390 	 * over as many bits of the seed as possible.
391 	 */
392 	seed = gethrtime();
393 	for (i = 0; i < ifsp->if_hwlen; i++)
394 		seed += ifsp->if_hwaddr[i] << ((i % 7) * 4);
395 	seed ^= getpid();
396 	srand48(seed);
397 
398 	dhcpmsg(MSG_DEBUG, "insert_ifs: inserted interface %s", if_name);
399 	return (ifsp);
400 
401 failure:
402 	free_ifs(ifsp);
403 	return (NULL);
404 }
405 
406 /*
407  * init_ifs(): puts an ifs in its initial state
408  *
409  *   input: struct ifslist *: the ifs to initialize
410  *  output: void
411  *    note: if the interface isn't fresh, use reset_ifs()
412  */
413 
414 static void
415 init_ifs(struct ifslist *ifsp)
416 {
417 	/*
418 	 * if_sock_ip_fd is created and bound in configure_if().
419 	 * if_sock_fd is bound in configure_if(); see comments in
420 	 * bound.c for more details on why.  if creation of if_sock_fd
421 	 * fails, we'll need more context anyway, so don't check.
422 	 */
423 
424 	ifsp->if_sock_fd	= socket(AF_INET, SOCK_DGRAM, 0);
425 	ifsp->if_sock_ip_fd	= -1;
426 	ifsp->if_state		= INIT;
427 	ifsp->if_routers	= NULL;
428 	ifsp->if_nrouters	= 0;
429 	ifsp->if_ack		= NULL;
430 	ifsp->if_orig_ack	= NULL;
431 	ifsp->if_server.s_addr  = htonl(INADDR_BROADCAST);
432 	ifsp->if_neg_monosec 	= monosec();
433 	ifsp->if_lease 		= 0;
434 	ifsp->if_t1 		= 0;
435 	ifsp->if_t2 		= 0;
436 	ifsp->if_reqhost	= NULL;
437 
438 	ifsp->if_script_helper_pid	= -1;
439 	ifsp->if_script_callback	= NULL;
440 	ifsp->if_script_event		= NULL;
441 	ifsp->if_callback_msg		= NULL;
442 	ifsp->if_script_event_id	= -1;
443 	ifsp->if_script_pid		= -1;
444 	ifsp->if_script_fd		= -1;
445 
446 	ifsp->if_offer_id		= -1;
447 	ifsp->if_acknak_id		= -1;
448 	ifsp->if_acknak_bcast_id	= -1;
449 	ifsp->if_timer[DHCP_T1_TIMER]	= -1;
450 	ifsp->if_timer[DHCP_T2_TIMER]   = -1;
451 	ifsp->if_timer[DHCP_LEASE_TIMER] = -1;
452 	ifsp->if_offer_timer		= -1;
453 
454 	set_packet_filter(ifsp->if_dlpi_fd, dhcp_filter, NULL, "DHCP");
455 
456 	dhcpmsg(MSG_DEBUG, "init_ifs: initted interface %s", ifsp->if_name);
457 }
458 
459 /*
460  * remove_ifs_default_routes(): removes an ifs's default routes
461  *
462  *   input: struct ifslist *: the ifs whose default routes need to be removed
463  *  output: void
464  */
465 
466 static void
467 remove_ifs_default_routes(struct ifslist *ifsp)
468 {
469 	if (ifsp->if_routers != NULL) {
470 		while (ifsp->if_nrouters > 0) {
471 			(void) del_default_route(ifsp->if_name,
472 			    &ifsp->if_routers[--ifsp->if_nrouters]);
473 		}
474 		free(ifsp->if_routers);
475 		ifsp->if_routers = NULL;
476 	}
477 }
478 
479 /*
480  * reset_ifs(): resets an ifs to its initial state
481  *
482  *   input: struct ifslist *: the ifs to reset
483  *  output: void
484  */
485 
486 void
487 reset_ifs(struct ifslist *ifsp)
488 {
489 	ifsp->if_dflags &= ~DHCP_IF_FAILED;
490 
491 	remove_ifs_default_routes(ifsp);
492 
493 	if (ifsp->if_sock_fd != -1)
494 		(void) close(ifsp->if_sock_fd);
495 
496 	if (ifsp->if_orig_ack != ifsp->if_ack)
497 		free_pkt_list(&ifsp->if_orig_ack);
498 
499 	free_pkt_list(&ifsp->if_ack);
500 
501 	if (ifsp->if_sock_ip_fd != -1)
502 		(void) close(ifsp->if_sock_ip_fd);
503 
504 	if (ifsp->if_offer_id != -1) {
505 		if (iu_unregister_event(eh, ifsp->if_offer_id, NULL) != 0)
506 			(void) release_ifs(ifsp);
507 	}
508 
509 	(void) unregister_acknak(ifsp);		/* just in case */
510 
511 	cancel_ifs_timers(ifsp);
512 
513 	if (ifsp->if_offer_timer != -1) {
514 		if (iu_cancel_timer(tq, ifsp->if_offer_timer, NULL))
515 			(void) release_ifs(ifsp);
516 	}
517 
518 	stop_pkt_retransmission(ifsp);
519 
520 	init_ifs(ifsp);
521 }
522 
523 /*
524  * lookup_ifs(): looks up an ifs, given its name
525  *
526  *   input: const char *: the name of the ifs entry (the interface name)
527  *			  the name "" searches for the primary interface
528  *  output: struct ifslist *: the corresponding ifs, or NULL if not found
529  */
530 
531 struct ifslist *
532 lookup_ifs(const char *if_name)
533 {
534 	struct ifslist	*ifs;
535 
536 	for (ifs = ifsheadp; ifs != NULL; ifs = ifs->next)
537 		if (*if_name != '\0') {
538 			if (strcmp(ifs->if_name, if_name) == 0)
539 				break;
540 		} else if (ifs->if_dflags & DHCP_IF_PRIMARY)
541 			break;
542 
543 	return (ifs);
544 }
545 
546 /*
547  * lookup_ifs_by_xid(): looks up an ifs, given its last used transaction id
548  *
549  *   input: int: the transaction id to look up
550  *  output: struct ifslist *: the corresponding ifs, or NULL if not found
551  */
552 
553 struct ifslist *
554 lookup_ifs_by_xid(uint32_t xid)
555 {
556 	struct ifslist *ifs;
557 
558 	for (ifs = ifsheadp; ifs != NULL; ifs = ifs->next) {
559 		if (ifs->if_send_pkt.pkt->xid == xid)
560 			break;
561 	}
562 
563 	return (ifs);
564 }
565 
566 /*
567  * remove_ifs(): removes a given ifs from the ifslist.  marks the ifs
568  *		 for being freed (but may not actually free it).
569  *
570  *   input: struct ifslist *: the ifs to remove
571  *  output: void
572  *    note: see interface.h for a discussion of ifs memory management
573  */
574 
575 void
576 remove_ifs(struct ifslist *ifsp)
577 {
578 	struct ifreq	ifr;
579 
580 	if (ifsp->if_dflags & DHCP_IF_REMOVED)
581 		return;
582 
583 	(void) memset(&ifr, 0, sizeof (struct ifreq));
584 	(void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
585 
586 	if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == 0) {
587 		ifr.ifr_flags &= ~IFF_DHCPRUNNING;
588 		(void) ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr);
589 	}
590 
591 	ifsp->if_dflags |= DHCP_IF_REMOVED;
592 
593 	/*
594 	 * if we have long term timers, cancel them so that interface
595 	 * resources can be reclaimed in a reasonable amount of time.
596 	 */
597 
598 	cancel_ifs_timers(ifsp);
599 
600 	if (ifsp->prev != NULL)
601 		ifsp->prev->next = ifsp->next;
602 	else
603 		ifsheadp = ifsp->next;
604 
605 	if (ifsp->next != NULL)
606 		ifsp->next->prev = ifsp->prev;
607 
608 	ifscount--;
609 	(void) release_ifs(ifsp);
610 
611 	/* no big deal if this fails */
612 	if (ifscount == 0) {
613 		inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT,
614 		    inactivity_shutdown, NULL);
615 	}
616 }
617 
618 /*
619  * hold_ifs(): acquires a hold on an ifs
620  *
621  *   input: struct ifslist *: the ifs entry to acquire a hold on
622  *  output: void
623  */
624 
625 void
626 hold_ifs(struct ifslist *ifsp)
627 {
628 	ifsp->if_hold_count++;
629 
630 	dhcpmsg(MSG_DEBUG2, "hold_ifs: hold count on %s: %d",
631 	    ifsp->if_name, ifsp->if_hold_count);
632 }
633 
634 /*
635  * release_ifs(): releases a hold previously acquired on an ifs.  if the
636  *		  hold count reaches 0, the ifs is freed
637  *
638  *   input: struct ifslist *: the ifs entry to release the hold on
639  *  output: int: the number of holds outstanding on the ifs
640  */
641 
642 int
643 release_ifs(struct ifslist *ifsp)
644 {
645 	if (ifsp->if_hold_count == 0) {
646 		dhcpmsg(MSG_CRIT, "release_ifs: extraneous release");
647 		return (0);
648 	}
649 
650 	if (--ifsp->if_hold_count == 0) {
651 		free_ifs(ifsp);
652 		return (0);
653 	}
654 
655 	dhcpmsg(MSG_DEBUG2, "release_ifs: hold count on %s: %d",
656 	    ifsp->if_name, ifsp->if_hold_count);
657 
658 	return (ifsp->if_hold_count);
659 }
660 
661 /*
662  * free_ifs(): frees the memory occupied by an ifs entry
663  *
664  *   input: struct ifslist *: the ifs entry to free
665  *  output: void
666  */
667 
668 static void
669 free_ifs(struct ifslist *ifsp)
670 {
671 	dhcpmsg(MSG_DEBUG, "free_ifs: freeing interface %s", ifsp->if_name);
672 
673 	free_pkt_list(&ifsp->if_recv_pkt_list);
674 	if (ifsp->if_ack != ifsp->if_orig_ack)
675 		free_pkt_list(&ifsp->if_orig_ack);
676 	free_pkt_list(&ifsp->if_ack);
677 	free(ifsp->if_send_pkt.pkt);
678 	free(ifsp->if_cid);
679 	free(ifsp->if_daddr);
680 	free(ifsp->if_hwaddr);
681 	free(ifsp->if_prl);
682 	free(ifsp->if_reqhost);
683 	free(ifsp->if_routers);
684 
685 	if (ifsp->if_sock_fd != -1)
686 		(void) close(ifsp->if_sock_fd);
687 
688 	if (ifsp->if_sock_ip_fd != -1)
689 		(void) close(ifsp->if_sock_ip_fd);
690 
691 	if (ifsp->if_dlpi_fd != -1)
692 		(void) dlpi_close(ifsp->if_dlpi_fd);
693 
694 	free(ifsp);
695 }
696 
697 /*
698  * checkaddr(): checks if the given address is still set on the given ifs
699  *
700  *   input: struct ifslist *: the ifs to check
701  *	    int: the address to lookup on the interface
702  *	    struct in_addr *: the address to compare to
703  *  output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
704  */
705 
706 static boolean_t
707 checkaddr(struct ifslist *ifsp, int ioccmd, struct in_addr *addr)
708 {
709 	struct ifreq		ifr;
710 	struct sockaddr_in 	*sin;
711 
712 	/* LINTED [ifr_addr is a sockaddr which will be aligned] */
713 	sin = (struct sockaddr_in *)&ifr.ifr_addr;
714 
715 	(void) memset(&ifr, 0, sizeof (struct ifreq));
716 	(void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
717 	ifr.ifr_addr.sa_family = AF_INET;
718 
719 	switch (ioctl(ifsp->if_sock_fd, ioccmd, &ifr)) {
720 	case 0:
721 		if (sin->sin_addr.s_addr != addr->s_addr)
722 			return (B_FALSE);
723 		break;
724 	case -1:
725 		if (errno == ENXIO)
726 			return (B_FALSE);
727 		break;
728 	}
729 	return (B_TRUE);
730 }
731 
732 /*
733  * verify_ifs(): verifies than an ifs is still valid (i.e., has not been
734  *		 explicitly or implicitly dropped or released)
735  *
736  *   input: struct ifslist *: the ifs to verify
737  *  output: int: 1 if the ifs is still valid, 0 if the interface is invalid
738  */
739 
740 int
741 verify_ifs(struct ifslist *ifsp)
742 {
743 	struct ifreq 		ifr;
744 
745 	if (ifsp->if_dflags & DHCP_IF_REMOVED)
746 		return (0);
747 
748 	(void) memset(&ifr, 0, sizeof (struct ifreq));
749 	(void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
750 
751 	ifr.ifr_addr.sa_family = AF_INET;
752 
753 	switch (ifsp->if_state) {
754 
755 	case BOUND:
756 	case RENEWING:
757 	case REBINDING:
758 
759 		/*
760 		 * if the interface has gone down or been unplumbed, then we
761 		 * act like there has been an implicit drop.
762 		 */
763 
764 		switch (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr)) {
765 		case 0:
766 			if ((ifr.ifr_flags & (IFF_UP|IFF_DHCPRUNNING)) !=
767 			    (IFF_UP|IFF_DHCPRUNNING))
768 				goto abandon;
769 			break;
770 		case -1:
771 			if (errno == ENXIO)
772 				goto abandon;
773 			break;
774 		}
775 		/* FALLTHRU */
776 
777 	case INIT_REBOOT:
778 	case SELECTING:
779 	case REQUESTING:
780 
781 		/*
782 		 * if the IP address, netmask, or broadcast address have
783 		 * changed, or the interface has been unplumbed, then we act
784 		 * like there has been an implicit drop.
785 		 */
786 
787 		if (!checkaddr(ifsp, SIOCGIFADDR, &ifsp->if_addr) ||
788 		    !checkaddr(ifsp, SIOCGIFNETMASK, &ifsp->if_netmask) ||
789 		    !checkaddr(ifsp, SIOCGIFBRDADDR, &ifsp->if_broadcast))
790 			goto abandon;
791 	}
792 
793 	return (1);
794 abandon:
795 	dhcpmsg(MSG_WARNING, "verify_ifs: %s has changed properties, "
796 	    "abandoning", ifsp->if_name);
797 
798 	remove_ifs(ifsp);
799 	return (0);
800 }
801 
802 /*
803  * canonize_ifs(): puts the interface in a canonical (zeroed) form
804  *
805  *   input: struct ifslist *: the interface to canonize
806  *  output: int: 1 on success, 0 on failure
807  */
808 
809 int
810 canonize_ifs(struct ifslist *ifsp)
811 {
812 	struct sockaddr_in	*sin;
813 	struct ifreq		ifr;
814 
815 	dhcpmsg(MSG_VERBOSE, "canonizing interface %s", ifsp->if_name);
816 
817 	/*
818 	 * note that due to infelicities in the routing code, any default
819 	 * routes must be removed prior to clearing the UP flag.
820 	 */
821 
822 	remove_ifs_default_routes(ifsp);
823 
824 	/* LINTED [ifr_addr is a sockaddr which will be aligned] */
825 	sin = (struct sockaddr_in *)&ifr.ifr_addr;
826 
827 	(void) memset(&ifr, 0, sizeof (struct ifreq));
828 	(void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
829 
830 	if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1)
831 		return (0);
832 
833 	/*
834 	 * clear the UP flag, but don't clear DHCPRUNNING since
835 	 * that should only be done when the interface is removed
836 	 * (see remove_ifs())
837 	 */
838 
839 	ifr.ifr_flags &= ~IFF_UP;
840 
841 	if (ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr) == -1)
842 		return (0);
843 
844 	/*
845 	 * since ifr is actually a union, we need to explicitly zero
846 	 * the flags field before we reuse the structure, or otherwise
847 	 * cruft may leak over into other members of the union.
848 	 */
849 
850 	ifr.ifr_flags = 0;
851 	ifr.ifr_addr.sa_family = AF_INET;
852 	sin->sin_addr.s_addr = htonl(INADDR_ANY);
853 
854 	if (ioctl(ifsp->if_sock_fd, SIOCSIFADDR, &ifr) == -1)
855 		return (0);
856 
857 	if (ioctl(ifsp->if_sock_fd, SIOCSIFNETMASK, &ifr) == -1)
858 		return (0);
859 
860 	if (ioctl(ifsp->if_sock_fd, SIOCSIFBRDADDR, &ifr) == -1)
861 		return (0);
862 
863 	/*
864 	 * any time we change the IP address, netmask, or broadcast we
865 	 * must be careful to also reset bookkeeping of what these are
866 	 * set to.  this is so we can detect if these characteristics
867 	 * are changed by another process.
868 	 */
869 
870 	ifsp->if_addr.s_addr	  = htonl(INADDR_ANY);
871 	ifsp->if_netmask.s_addr   = htonl(INADDR_ANY);
872 	ifsp->if_broadcast.s_addr = htonl(INADDR_ANY);
873 
874 	return (1);
875 }
876 
877 /*
878  * check_ifs(): makes sure an ifs is still valid, and if it is, releases the
879  *		ifs.  otherwise, it informs the caller the ifs is going away
880  *		and expects the caller to perform the release
881  *
882  *   input: struct ifslist *: the ifs to check
883  *  output: int: 1 if the interface is valid, 0 otherwise
884  */
885 
886 int
887 check_ifs(struct ifslist *ifsp)
888 {
889 	hold_ifs(ifsp);
890 	if (release_ifs(ifsp) == 1 || verify_ifs(ifsp) == 0) {
891 
892 		/*
893 		 * this interface is going away.  if there's an
894 		 * uncancelled IPC event roaming around, cancel it
895 		 * now.  we leave the hold on in case anyone else has
896 		 * any cleanup work that needs to be done before the
897 		 * interface goes away.
898 		 */
899 
900 		ipc_action_finish(ifsp, DHCP_IPC_E_UNKIF);
901 		async_finish(ifsp);
902 		return (0);
903 	}
904 
905 	(void) release_ifs(ifsp);
906 	return (1);
907 }
908 
909 /*
910  * nuke_ifslist(): delete the ifslist (for use when the dhcpagent is exiting)
911  *
912  *   input: boolean_t: B_TRUE if the agent is exiting due to SIGTERM
913  *  output: void
914  */
915 
916 void
917 nuke_ifslist(boolean_t onterm)
918 {
919 	int	status;
920 	struct ifslist	*ifsp, *ifsp_next;
921 
922 	for (ifsp = ifsheadp; ifsp != NULL; ifsp = ifsp_next) {
923 		ifsp_next = ifsp->next;
924 
925 		cancel_ifs_timers(ifsp);
926 		if (ifsp->if_script_pid != -1) {
927 			/* stop a script if it is not for DROP or RELEASE */
928 			if (strcmp(ifsp->if_script_event, EVENT_DROP) == 0 ||
929 			    strcmp(ifsp->if_script_event, EVENT_RELEASE) == 0) {
930 				continue;
931 			}
932 			script_stop(ifsp);
933 		}
934 
935 		/*
936 		 * if the script is started by script_start, dhcp_drop and
937 		 * dhcp_release should and will only be called after the
938 		 * script exits.
939 		 */
940 		if (onterm &&
941 		    df_get_bool(ifsp->if_name, DF_RELEASE_ON_SIGTERM)) {
942 			if (script_start(ifsp, EVENT_RELEASE, dhcp_release,
943 			    "DHCP agent is exiting", &status) == 1) {
944 				continue;
945 			}
946 			if (status == 1)
947 				continue;
948 		}
949 		(void) script_start(ifsp, EVENT_DROP, dhcp_drop, NULL, NULL);
950 	}
951 }
952 
953 /*
954  * refresh_ifslist(): refreshes all finite leases under DHCP control
955  *
956  *   input: iu_eh_t *: unused
957  *	    int: unused
958  *	    void *: unused
959  *  output: void
960  */
961 
962 /* ARGSUSED */
963 void
964 refresh_ifslist(iu_eh_t *eh, int sig, void *arg)
965 {
966 	struct ifslist *ifsp;
967 
968 	for (ifsp = ifsheadp; ifsp != NULL; ifsp = ifsp->next) {
969 
970 		if (ifsp->if_state != BOUND && ifsp->if_state != RENEWING &&
971 		    ifsp->if_state != REBINDING)
972 			continue;
973 
974 		if (ifsp->if_lease == DHCP_PERM)
975 			continue;
976 
977 		/*
978 		 * this interface has a finite lease and we do not know
979 		 * how long the machine's been off for.  refresh it.
980 		 */
981 
982 		dhcpmsg(MSG_WARNING, "refreshing lease on %s", ifsp->if_name);
983 		cancel_ifs_timer(ifsp, DHCP_T1_TIMER);
984 		cancel_ifs_timer(ifsp, DHCP_T2_TIMER);
985 		(void) iu_adjust_timer(tq, ifsp->if_timer[DHCP_LEASE_TIMER], 0);
986 	}
987 }
988 
989 /*
990  * ifs_count(): returns the number of interfaces currently managed
991  *
992  *   input: void
993  *  output: unsigned int: the number of interfaces currently managed
994  */
995 
996 unsigned int
997 ifs_count(void)
998 {
999 	return (ifscount);
1000 }
1001 
1002 /*
1003  * cancel_ifs_timer(): cancels a lease-related timer on an interface
1004  *
1005  *   input: struct ifslist *: the interface to operate on
1006  *	    int: the timer id of the timer to cancel
1007  *  output: void
1008  */
1009 
1010 static void
1011 cancel_ifs_timer(struct ifslist *ifsp, int timer_id)
1012 {
1013 	if (ifsp->if_timer[timer_id] != -1) {
1014 		if (iu_cancel_timer(tq, ifsp->if_timer[timer_id], NULL) == 1) {
1015 			(void) release_ifs(ifsp);
1016 			ifsp->if_timer[timer_id] = -1;
1017 		} else
1018 			dhcpmsg(MSG_WARNING, "cancel_ifs_timer: cannot cancel "
1019 			    "if_timer[%d]", timer_id);
1020 	}
1021 }
1022 
1023 /*
1024  * cancel_ifs_timers(): cancels an interface's pending lease-related timers
1025  *
1026  *   input: struct ifslist *: the interface to operate on
1027  *  output: void
1028  */
1029 
1030 void
1031 cancel_ifs_timers(struct ifslist *ifsp)
1032 {
1033 	cancel_ifs_timer(ifsp, DHCP_T1_TIMER);
1034 	cancel_ifs_timer(ifsp, DHCP_T2_TIMER);
1035 	cancel_ifs_timer(ifsp, DHCP_LEASE_TIMER);
1036 }
1037 
1038 /*
1039  * schedule_ifs_timer(): schedules a lease-related timer on an interface
1040  *
1041  *   input: struct ifslist *: the interface to operate on
1042  *	    int: the timer to schedule
1043  *	    uint32_t: the number of seconds in the future it should fire
1044  *	    iu_tq_callback_t *: the callback to call upon firing
1045  *  output: int: 1 if the timer was scheduled successfully, 0 on failure
1046  */
1047 
1048 int
1049 schedule_ifs_timer(struct ifslist *ifsp, int timer_id, uint32_t sec,
1050     iu_tq_callback_t *expire)
1051 {
1052 	cancel_ifs_timer(ifsp, timer_id);		/* just in case */
1053 
1054 	ifsp->if_timer[timer_id] = iu_schedule_timer(tq, sec, expire, ifsp);
1055 	if (ifsp->if_timer[timer_id] == -1) {
1056 		dhcpmsg(MSG_WARNING, "schedule_ifs_timer: cannot schedule "
1057 		    "if_timer[%d]", timer_id);
1058 		return (0);
1059 	}
1060 
1061 	hold_ifs(ifsp);
1062 	return (1);
1063 }
1064 
1065 /*
1066  * Get the value of the named property on the named node in devinfo root.
1067  *
1068  *   input: const char *: The name of the node containing the property.
1069  *	    const char *: The name of the property.
1070  *	    uchar_t **: The property value, modified iff B_TRUE is returned.
1071  *                      If no value is found the value is set to NULL.
1072  *	    unsigned int *: The length of the property value
1073  *  output: boolean_t: Returns B_TRUE if successful (no problems),
1074  *                     otherwise B_FALSE.
1075  *    note: The memory allocated by this function must be freed by
1076  *          the caller. This code is derived from
1077  *          usr/src/lib/libwanboot/common/bootinfo_aux.c.
1078  */
1079 
1080 static boolean_t
1081 get_prom_prop(const char *nodename, const char *propname, uchar_t **propvaluep,
1082     unsigned int *lenp)
1083 {
1084 	di_node_t		root_node = DI_NODE_NIL;
1085 	di_node_t		node;
1086 	di_prom_handle_t	phdl = DI_PROM_HANDLE_NIL;
1087 	di_prom_prop_t		pp;
1088 	uchar_t			*value = NULL;
1089 	unsigned int		len = 0;
1090 	boolean_t		success = B_TRUE;
1091 
1092 	/*
1093 	 * locate root node
1094 	 */
1095 
1096 	if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL ||
1097 	    (phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) {
1098 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property root node "
1099 		    "not found");
1100 		goto get_prom_prop_cleanup;
1101 	}
1102 
1103 	/*
1104 	 * locate nodename within '/'
1105 	 */
1106 
1107 	for (node = di_child_node(root_node);
1108 	    node != DI_NODE_NIL;
1109 	    node = di_sibling_node(node)) {
1110 		if (strcmp(di_node_name(node), nodename) == 0) {
1111 			break;
1112 		}
1113 	}
1114 
1115 	if (node == DI_NODE_NIL) {
1116 		dhcpmsg(MSG_DEBUG, "get_prom_prop: node not found");
1117 		goto get_prom_prop_cleanup;
1118 	}
1119 
1120 	/*
1121 	 * scan all properties of /nodename for the 'propname' property
1122 	 */
1123 
1124 	for (pp = di_prom_prop_next(phdl, node, DI_PROM_PROP_NIL);
1125 	    pp != DI_PROM_PROP_NIL;
1126 	    pp = di_prom_prop_next(phdl, node, pp)) {
1127 
1128 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property = %s",
1129 		    di_prom_prop_name(pp));
1130 
1131 		if (strcmp(propname, di_prom_prop_name(pp)) == 0) {
1132 			break;
1133 		}
1134 	}
1135 
1136 	if (pp == DI_PROM_PROP_NIL) {
1137 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property not found");
1138 		goto get_prom_prop_cleanup;
1139 	}
1140 
1141 	/*
1142 	 * get the property; allocate some memory copy it out
1143 	 */
1144 
1145 	len = di_prom_prop_data(pp, (uchar_t **)&value);
1146 
1147 	if (value == NULL) {
1148 		/*
1149 		 * property data read problems
1150 		 */
1151 
1152 		success = B_FALSE;
1153 		dhcpmsg(MSG_ERR, "get_prom_prop: cannot read property data");
1154 		goto get_prom_prop_cleanup;
1155 	}
1156 
1157 	if (propvaluep != NULL) {
1158 		/*
1159 		 * allocate somewhere to copy the property value to
1160 		 */
1161 
1162 		*propvaluep = calloc(len, sizeof (uchar_t));
1163 
1164 		if (*propvaluep == NULL) {
1165 			/*
1166 			 * allocation problems
1167 			 */
1168 
1169 			success = B_FALSE;
1170 			dhcpmsg(MSG_ERR, "get_prom_prop: cannot allocate "
1171 			    "memory for property value");
1172 			goto get_prom_prop_cleanup;
1173 		}
1174 
1175 		/*
1176 		 * copy data out
1177 		 */
1178 
1179 		(void) memcpy(*propvaluep, value, len);
1180 
1181 		/*
1182 		 * copy out the length if a suitable pointer has
1183 		 * been supplied
1184 		 */
1185 
1186 		if (lenp != NULL) {
1187 			*lenp = len;
1188 		}
1189 
1190 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property value "
1191 		    "length = %d", len);
1192 	}
1193 
1194 get_prom_prop_cleanup:
1195 
1196 	if (phdl != DI_PROM_HANDLE_NIL) {
1197 		di_prom_fini(phdl);
1198 	}
1199 
1200 	if (root_node != DI_NODE_NIL) {
1201 		di_fini(root_node);
1202 	}
1203 
1204 	return (success);
1205 }
1206