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