xref: /titanic_50/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c (revision c77a61a72b5ecdc507d6cf104142edd371a16c84)
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, SIOCGIFINDEX, &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: SIOCGIFINDEX for %s", if_name);
200 		goto failure;
201 	}
202 	ifsp->if_index = ifr.ifr_index;
203 
204 	if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
205 		if (errno == ENXIO)
206 			*error = DHCP_IPC_E_INVIF;
207 		else
208 			*error = DHCP_IPC_E_INT;
209 		dhcpmsg(MSG_ERR, "insert_ifs: SIOCGIFFLAGS for %s", if_name);
210 		goto failure;
211 	}
212 
213 	/*
214 	 * if DHCPRUNNING is already set on the interface and we're
215 	 * not adopting it, the agent probably crashed and burned.
216 	 * note it, but don't let it stop the proceedings.  we're
217 	 * pretty sure we're not already running, since we wouldn't
218 	 * have been able to bind to our IPC port.
219 	 */
220 
221 	if ((is_adopting == B_FALSE) && (ifr.ifr_flags & IFF_DHCPRUNNING))
222 		dhcpmsg(MSG_WARNING, "insert_ifs: DHCP flag already set on %s",
223 		    if_name);
224 
225 	ifr.ifr_flags |= IFF_DHCPRUNNING;
226 	(void) ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr);
227 
228 	ifsp->if_send_pkt.pkt = calloc(ifsp->if_max, 1);
229 	if (ifsp->if_send_pkt.pkt == NULL) {
230 		dhcpmsg(MSG_ERR, "insert_ifs: cannot allocate if_send_pkt "
231 		    "for %s", if_name);
232 		*error = DHCP_IPC_E_MEMORY;
233 		goto failure;
234 	}
235 
236 	if (is_adopting) {
237 		/*
238 		 * if the agent is adopting a lease OBP is initially
239 		 * searched for a client-id
240 		 */
241 
242 		dhcpmsg(MSG_DEBUG, "insert_ifs: getting /chosen:clientid "
243 		    "property");
244 
245 		if (!get_prom_prop("chosen", "client-id", &ifsp->if_cid,
246 		    &client_id_len)) {
247 			/*
248 			 * a failure occurred trying to acquire the client-id
249 			 */
250 
251 			dhcpmsg(MSG_DEBUG, "insert_ifs: cannot allocate client "
252 			    "id for %s", if_name);
253 			*error = DHCP_IPC_E_INT;
254 			goto failure;
255 		} else if (dlia->dl_mac_type == DL_IB && ifsp->if_cid == NULL) {
256 			/*
257 			 * when the interface is infiniband and the agent
258 			 * is adopting the lease there must be an OBP
259 			 * client-id.
260 			 */
261 
262 			dhcpmsg(MSG_DEBUG, "insert_ifs: no /chosen:clientid"
263 			    "id for %s", if_name);
264 			*error = DHCP_IPC_E_INT;
265 			goto failure;
266 		}
267 
268 		ifsp->if_cidlen = client_id_len;
269 	} else {
270 		/*
271 		 * look in defaults file for the client-id
272 		 */
273 
274 		dhcpmsg(MSG_DEBUG, "insert_ifs: getting defaults client-id "
275 		    "property");
276 
277 		client_id = df_get_octet(if_name, DF_CLIENT_ID, &client_id_len);
278 
279 		/*
280 		 * at this point, all logical interfaces must be explicitly
281 		 * configured with a client id by the administrator.
282 		 */
283 
284 		if (client_id == NULL && strchr(if_name, ':') != NULL) {
285 			dhcpmsg(MSG_ERROR, "no client id configured for "
286 			    "logical interface %s; cannot manage", if_name);
287 			*error = DHCP_IPC_E_NOIFCID;
288 			goto failure;
289 		}
290 
291 		if (client_id != NULL) {
292 			/*
293 			 * the defaults client-id value must be copied out to
294 			 * another buffer
295 			 */
296 
297 			ifsp->if_cid = calloc(client_id_len, sizeof (uchar_t));
298 
299 			if (ifsp->if_cid == NULL) {
300 				dhcpmsg(MSG_ERR, "insert_ifs: cannot "
301 				    "allocate client id for %s", if_name);
302 				*error = DHCP_IPC_E_MEMORY;
303 				goto failure;
304 			}
305 
306 			(void) memcpy(ifsp->if_cid, client_id, client_id_len);
307 
308 			ifsp->if_cidlen = client_id_len;
309 		} else if (dlia->dl_mac_type == DL_IB) {
310 			/*
311 			 * This comes from DHCP over IPoIB spec. In the absence
312 			 * of an user specified client id, IPoIB automatically
313 			 * uses the required format, with the unique 4 octet
314 			 * value set to 0 (since IPoIB driver allows only a
315 			 * single interface on a port with a specific GID to
316 			 * belong to an IP subnet (PSARC 2001/289,
317 			 * FWARC 2002/702).
318 			 *
319 			 *   Type  Client-Identifier
320 			 * +-----+-----+-----+-----+-----+----....----+
321 			 * |  0  |  0 (4 octets)   |   GID (16 octets)|
322 			 * +-----+-----+-----+-----+-----+----....----+
323 			 */
324 			ifsp->if_cidlen = 1 + 4 + 16;
325 			ifsp->if_cid = client_id = malloc(ifsp->if_cidlen);
326 			if (ifsp->if_cid == NULL) {
327 				dhcpmsg(MSG_ERR, "insert_ifs: cannot "
328 				    "allocate client id for %s", if_name);
329 				*error = DHCP_IPC_E_MEMORY;
330 				goto failure;
331 			}
332 
333 			/*
334 			 * Pick the GID from the mac address. The format
335 			 * of the hardware address is:
336 			 * +-----+-----+-----+-----+----....----+
337 			 * | QPN (4 octets)  |   GID (16 octets)|
338 			 * +-----+-----+-----+-----+----....----+
339 			 */
340 			(void) memcpy(client_id + 5, ifsp->if_hwaddr + 4,
341 			    ifsp->if_hwlen - 4);
342 			(void) memset(client_id, 0, 5);
343 		}
344 	}
345 
346 	/*
347 	 * initialize the parameter request list, if there is one.
348 	 */
349 
350 	prl = df_get_string(if_name, DF_PARAM_REQUEST_LIST);
351 	if (prl == NULL)
352 		ifsp->if_prl = NULL;
353 	else {
354 		for (ifsp->if_prllen = 1, i = 0; prl[i] != '\0'; i++)
355 			if (prl[i] == ',')
356 				ifsp->if_prllen++;
357 
358 		ifsp->if_prl = malloc(ifsp->if_prllen);
359 		if (ifsp->if_prl == NULL) {
360 			dhcpmsg(MSG_WARNING, "insert_ifs: cannot allocate "
361 			    "parameter request list for %s (continuing)",
362 			    if_name);
363 		} else {
364 			for (i = 0; i < ifsp->if_prllen; prl++, i++) {
365 				ifsp->if_prl[i] = strtoul(prl, NULL, 0);
366 				while (*prl != ',' && *prl != '\0')
367 					prl++;
368 				if (*prl == '\0')
369 					break;
370 			}
371 		}
372 	}
373 
374 	ifsp->if_offer_wait = df_get_int(if_name, DF_OFFER_WAIT);
375 
376 	/*
377 	 * we're past the point of failure; chain it on.
378 	 */
379 
380 	ifsp->next	= ifsheadp;
381 	ifsp->prev	= NULL;
382 	ifsheadp	= ifsp;
383 
384 	if (ifsheadp->next != NULL)
385 		ifsheadp->next->prev = ifsheadp;
386 
387 	hold_ifs(ifsp);
388 	ifscount++;
389 
390 	if (inactivity_id != -1) {
391 		if (iu_cancel_timer(tq, inactivity_id, NULL) == 1)
392 			inactivity_id = -1;
393 	}
394 
395 	/*
396 	 * seed the random number generator, since we're going to need it
397 	 * to set transaction id's and for exponential backoff.  if an
398 	 * interface is already initialized, then we just end up harmlessly
399 	 * reseeding it.  note that we try to spread the hardware address
400 	 * over as many bits of the seed as possible.
401 	 */
402 	seed = gethrtime();
403 	for (i = 0; i < ifsp->if_hwlen; i++)
404 		seed += ifsp->if_hwaddr[i] << ((i % 7) * 4);
405 	seed ^= getpid();
406 	srand48(seed);
407 
408 	dhcpmsg(MSG_DEBUG, "insert_ifs: inserted interface %s", if_name);
409 	return (ifsp);
410 
411 failure:
412 	free_ifs(ifsp);
413 	return (NULL);
414 }
415 
416 /*
417  * init_ifs(): puts an ifs in its initial state
418  *
419  *   input: struct ifslist *: the ifs to initialize
420  *  output: void
421  *    note: if the interface isn't fresh, use reset_ifs()
422  */
423 
424 static void
425 init_ifs(struct ifslist *ifsp)
426 {
427 	/*
428 	 * if_sock_ip_fd is created and bound in configure_if().
429 	 * if_sock_fd is bound in configure_if(); see comments in
430 	 * bound.c for more details on why.  if creation of if_sock_fd
431 	 * fails, we'll need more context anyway, so don't check.
432 	 */
433 
434 	ifsp->if_sock_fd	= socket(AF_INET, SOCK_DGRAM, 0);
435 	ifsp->if_sock_ip_fd	= -1;
436 	ifsp->if_state		= INIT;
437 	ifsp->if_routers	= NULL;
438 	ifsp->if_nrouters	= 0;
439 	ifsp->if_ack		= NULL;
440 	ifsp->if_orig_ack	= NULL;
441 	ifsp->if_server.s_addr  = htonl(INADDR_BROADCAST);
442 	ifsp->if_neg_monosec 	= monosec();
443 	ifsp->if_lease 		= 0;
444 	ifsp->if_t1 		= 0;
445 	ifsp->if_t2 		= 0;
446 	ifsp->if_reqhost	= NULL;
447 
448 	ifsp->if_script_helper_pid	= -1;
449 	ifsp->if_script_callback	= NULL;
450 	ifsp->if_script_event		= NULL;
451 	ifsp->if_callback_msg		= NULL;
452 	ifsp->if_script_event_id	= -1;
453 	ifsp->if_script_pid		= -1;
454 	ifsp->if_script_fd		= -1;
455 
456 	ifsp->if_offer_id		= -1;
457 	ifsp->if_acknak_id		= -1;
458 	ifsp->if_acknak_bcast_id	= -1;
459 	ifsp->if_timer[DHCP_T1_TIMER]	= -1;
460 	ifsp->if_timer[DHCP_T2_TIMER]   = -1;
461 	ifsp->if_timer[DHCP_LEASE_TIMER] = -1;
462 	ifsp->if_offer_timer		= -1;
463 
464 	set_packet_filter(ifsp->if_dlpi_fd, dhcp_filter, NULL, "DHCP");
465 
466 	dhcpmsg(MSG_DEBUG, "init_ifs: initted interface %s", ifsp->if_name);
467 }
468 
469 /*
470  * remove_ifs_default_routes(): removes an ifs's default routes
471  *
472  *   input: struct ifslist *: the ifs whose default routes need to be removed
473  *  output: void
474  */
475 
476 static void
477 remove_ifs_default_routes(struct ifslist *ifsp)
478 {
479 	if (ifsp->if_routers != NULL) {
480 		while (ifsp->if_nrouters > 0) {
481 			(void) del_default_route(ifsp->if_name,
482 			    &ifsp->if_routers[--ifsp->if_nrouters]);
483 		}
484 		free(ifsp->if_routers);
485 		ifsp->if_routers = NULL;
486 	}
487 }
488 
489 /*
490  * reset_ifs(): resets an ifs to its initial state
491  *
492  *   input: struct ifslist *: the ifs to reset
493  *  output: void
494  */
495 
496 void
497 reset_ifs(struct ifslist *ifsp)
498 {
499 	ifsp->if_dflags &= ~DHCP_IF_FAILED;
500 
501 	remove_ifs_default_routes(ifsp);
502 
503 	if (ifsp->if_sock_fd != -1)
504 		(void) close(ifsp->if_sock_fd);
505 
506 	if (ifsp->if_orig_ack != ifsp->if_ack)
507 		free_pkt_list(&ifsp->if_orig_ack);
508 
509 	free_pkt_list(&ifsp->if_ack);
510 
511 	if (ifsp->if_sock_ip_fd != -1)
512 		(void) close(ifsp->if_sock_ip_fd);
513 
514 	if (ifsp->if_offer_id != -1) {
515 		if (iu_unregister_event(eh, ifsp->if_offer_id, NULL) != 0)
516 			(void) release_ifs(ifsp);
517 	}
518 
519 	(void) unregister_acknak(ifsp);		/* just in case */
520 
521 	cancel_ifs_timers(ifsp);
522 
523 	if (ifsp->if_offer_timer != -1) {
524 		if (iu_cancel_timer(tq, ifsp->if_offer_timer, NULL))
525 			(void) release_ifs(ifsp);
526 	}
527 
528 	stop_pkt_retransmission(ifsp);
529 
530 	init_ifs(ifsp);
531 }
532 
533 /*
534  * lookup_ifs(): looks up an ifs, given its name
535  *
536  *   input: const char *: the name of the ifs entry (the interface name)
537  *			  the name "" searches for the primary interface
538  *  output: struct ifslist *: the corresponding ifs, or NULL if not found
539  */
540 
541 struct ifslist *
542 lookup_ifs(const char *if_name)
543 {
544 	struct ifslist	*ifs;
545 
546 	for (ifs = ifsheadp; ifs != NULL; ifs = ifs->next)
547 		if (*if_name != '\0') {
548 			if (strcmp(ifs->if_name, if_name) == 0)
549 				break;
550 		} else if (ifs->if_dflags & DHCP_IF_PRIMARY)
551 			break;
552 
553 	return (ifs);
554 }
555 
556 /*
557  * lookup_ifs_by_xid(): looks up an ifs, given its last used transaction id
558  *
559  *   input: int: the transaction id to look up
560  *  output: struct ifslist *: the corresponding ifs, or NULL if not found
561  */
562 
563 struct ifslist *
564 lookup_ifs_by_xid(uint32_t xid)
565 {
566 	struct ifslist *ifs;
567 
568 	for (ifs = ifsheadp; ifs != NULL; ifs = ifs->next) {
569 		if (ifs->if_send_pkt.pkt->xid == xid)
570 			break;
571 	}
572 
573 	return (ifs);
574 }
575 
576 /*
577  * lookup_ifs_by_uindex(): Looks up ifs entries given truncated index and
578  *			   previous ifs pointer (or NULL for list start).
579  *			   Caller is expected to iterate through all
580  *			   potential matches to find interface of interest.
581  *
582  *   input: int: the interface index
583  *	    struct ifslist *: the previous ifs, or NULL for list start
584  *  output: struct ifslist *: the next matching ifs, or NULL if not found
585  *    note: This operates using the 'truncated' (16-bit) ifindex as seen by
586  *	    routing socket clients.  The value stored in if_index is the
587  *	    32-bit ifindex from the ioctl interface.
588  */
589 
590 struct ifslist *
591 lookup_ifs_by_uindex(uint16_t ifindex, struct ifslist *ifs)
592 {
593 	if (ifs == NULL)
594 		ifs = ifsheadp;
595 	else
596 		ifs = ifs->next;
597 
598 	for (; ifs != NULL; ifs = ifs->next) {
599 		if ((ifs->if_index & 0xffff) == ifindex)
600 			break;
601 	}
602 
603 	return (ifs);
604 }
605 
606 /*
607  * remove_ifs(): removes a given ifs from the ifslist.  marks the ifs
608  *		 for being freed (but may not actually free it).
609  *
610  *   input: struct ifslist *: the ifs to remove
611  *  output: void
612  *    note: see interface.h for a discussion of ifs memory management
613  */
614 
615 void
616 remove_ifs(struct ifslist *ifsp)
617 {
618 	struct ifreq	ifr;
619 
620 	if (ifsp->if_dflags & DHCP_IF_REMOVED)
621 		return;
622 
623 	(void) memset(&ifr, 0, sizeof (struct ifreq));
624 	(void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
625 
626 	if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == 0) {
627 		ifr.ifr_flags &= ~IFF_DHCPRUNNING;
628 		(void) ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr);
629 	}
630 
631 	ifsp->if_dflags |= DHCP_IF_REMOVED;
632 
633 	/*
634 	 * if we have long term timers, cancel them so that interface
635 	 * resources can be reclaimed in a reasonable amount of time.
636 	 */
637 
638 	cancel_ifs_timers(ifsp);
639 
640 	if (ifsp->prev != NULL)
641 		ifsp->prev->next = ifsp->next;
642 	else
643 		ifsheadp = ifsp->next;
644 
645 	if (ifsp->next != NULL)
646 		ifsp->next->prev = ifsp->prev;
647 
648 	ifscount--;
649 	(void) release_ifs(ifsp);
650 
651 	/* no big deal if this fails */
652 	if (ifscount == 0) {
653 		inactivity_id = iu_schedule_timer(tq, DHCP_INACTIVITY_WAIT,
654 		    inactivity_shutdown, NULL);
655 	}
656 }
657 
658 /*
659  * hold_ifs(): acquires a hold on an ifs
660  *
661  *   input: struct ifslist *: the ifs entry to acquire a hold on
662  *  output: void
663  */
664 
665 void
666 hold_ifs(struct ifslist *ifsp)
667 {
668 	ifsp->if_hold_count++;
669 
670 	dhcpmsg(MSG_DEBUG2, "hold_ifs: hold count on %s: %d",
671 	    ifsp->if_name, ifsp->if_hold_count);
672 }
673 
674 /*
675  * release_ifs(): releases a hold previously acquired on an ifs.  if the
676  *		  hold count reaches 0, the ifs is freed
677  *
678  *   input: struct ifslist *: the ifs entry to release the hold on
679  *  output: int: the number of holds outstanding on the ifs
680  */
681 
682 int
683 release_ifs(struct ifslist *ifsp)
684 {
685 	if (ifsp->if_hold_count == 0) {
686 		dhcpmsg(MSG_CRIT, "release_ifs: extraneous release");
687 		return (0);
688 	}
689 
690 	if (--ifsp->if_hold_count == 0) {
691 		free_ifs(ifsp);
692 		return (0);
693 	}
694 
695 	dhcpmsg(MSG_DEBUG2, "release_ifs: hold count on %s: %d",
696 	    ifsp->if_name, ifsp->if_hold_count);
697 
698 	return (ifsp->if_hold_count);
699 }
700 
701 /*
702  * free_ifs(): frees the memory occupied by an ifs entry
703  *
704  *   input: struct ifslist *: the ifs entry to free
705  *  output: void
706  */
707 
708 static void
709 free_ifs(struct ifslist *ifsp)
710 {
711 	dhcpmsg(MSG_DEBUG, "free_ifs: freeing interface %s", ifsp->if_name);
712 
713 	free_pkt_list(&ifsp->if_recv_pkt_list);
714 	if (ifsp->if_ack != ifsp->if_orig_ack)
715 		free_pkt_list(&ifsp->if_orig_ack);
716 	free_pkt_list(&ifsp->if_ack);
717 	free(ifsp->if_send_pkt.pkt);
718 	free(ifsp->if_cid);
719 	free(ifsp->if_daddr);
720 	free(ifsp->if_hwaddr);
721 	free(ifsp->if_prl);
722 	free(ifsp->if_reqhost);
723 	free(ifsp->if_routers);
724 
725 	if (ifsp->if_sock_fd != -1)
726 		(void) close(ifsp->if_sock_fd);
727 
728 	if (ifsp->if_sock_ip_fd != -1)
729 		(void) close(ifsp->if_sock_ip_fd);
730 
731 	if (ifsp->if_dlpi_fd != -1)
732 		(void) dlpi_close(ifsp->if_dlpi_fd);
733 
734 	free(ifsp);
735 }
736 
737 /*
738  * checkaddr(): checks if the given address is still set on the given ifs
739  *
740  *   input: struct ifslist *: the ifs to check
741  *	    int: the address to lookup on the interface
742  *	    struct in_addr *: the address to compare to
743  *  output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
744  */
745 
746 static boolean_t
747 checkaddr(struct ifslist *ifsp, int ioccmd, struct in_addr *addr)
748 {
749 	struct ifreq		ifr;
750 	struct sockaddr_in 	*sin;
751 
752 	/* LINTED [ifr_addr is a sockaddr which will be aligned] */
753 	sin = (struct sockaddr_in *)&ifr.ifr_addr;
754 
755 	(void) memset(&ifr, 0, sizeof (struct ifreq));
756 	(void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
757 	ifr.ifr_addr.sa_family = AF_INET;
758 
759 	switch (ioctl(ifsp->if_sock_fd, ioccmd, &ifr)) {
760 	case 0:
761 		if (sin->sin_addr.s_addr != addr->s_addr)
762 			return (B_FALSE);
763 		break;
764 	case -1:
765 		if (errno == ENXIO)
766 			return (B_FALSE);
767 		break;
768 	}
769 	return (B_TRUE);
770 }
771 
772 /*
773  * verify_ifs(): verifies than an ifs is still valid (i.e., has not been
774  *		 explicitly or implicitly dropped or released)
775  *
776  *   input: struct ifslist *: the ifs to verify
777  *  output: int: 1 if the ifs is still valid, 0 if the interface is invalid
778  */
779 
780 int
781 verify_ifs(struct ifslist *ifsp)
782 {
783 	struct ifreq 		ifr;
784 
785 	if (ifsp->if_dflags & DHCP_IF_REMOVED)
786 		return (0);
787 
788 	(void) memset(&ifr, 0, sizeof (struct ifreq));
789 	(void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
790 
791 	ifr.ifr_addr.sa_family = AF_INET;
792 
793 	switch (ifsp->if_state) {
794 
795 	case BOUND:
796 	case RENEWING:
797 	case REBINDING:
798 
799 		/*
800 		 * if the interface has gone down or been unplumbed, then we
801 		 * act like there has been an implicit drop.
802 		 */
803 
804 		switch (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr)) {
805 		case 0:
806 			if ((ifr.ifr_flags & (IFF_UP|IFF_DHCPRUNNING)) !=
807 			    (IFF_UP|IFF_DHCPRUNNING))
808 				goto abandon;
809 			break;
810 		case -1:
811 			if (errno == ENXIO)
812 				goto abandon;
813 			break;
814 		}
815 		switch (ioctl(ifsp->if_sock_fd, SIOCGIFINDEX, &ifr)) {
816 		case 0:
817 			if (ifr.ifr_index != ifsp->if_index)
818 				goto abandon;
819 			break;
820 		case -1:
821 			if (errno == ENXIO)
822 				goto abandon;
823 			break;
824 		}
825 		/* FALLTHRU */
826 
827 	case INIT_REBOOT:
828 	case SELECTING:
829 	case REQUESTING:
830 
831 		/*
832 		 * if the IP address, netmask, or broadcast address have
833 		 * changed, or the interface has been unplumbed, then we act
834 		 * like there has been an implicit drop.
835 		 */
836 
837 		if (!checkaddr(ifsp, SIOCGIFADDR, &ifsp->if_addr) ||
838 		    !checkaddr(ifsp, SIOCGIFNETMASK, &ifsp->if_netmask) ||
839 		    !checkaddr(ifsp, SIOCGIFBRDADDR, &ifsp->if_broadcast))
840 			goto abandon;
841 	}
842 
843 	return (1);
844 abandon:
845 	dhcpmsg(MSG_WARNING, "verify_ifs: %s has changed properties, "
846 	    "abandoning", ifsp->if_name);
847 
848 	remove_ifs(ifsp);
849 	return (0);
850 }
851 
852 /*
853  * canonize_ifs(): puts the interface in a canonical (zeroed) form
854  *
855  *   input: struct ifslist *: the interface to canonize
856  *  output: int: 1 on success, 0 on failure
857  */
858 
859 int
860 canonize_ifs(struct ifslist *ifsp)
861 {
862 	struct sockaddr_in	*sin;
863 	struct ifreq		ifr;
864 
865 	dhcpmsg(MSG_VERBOSE, "canonizing interface %s", ifsp->if_name);
866 
867 	/*
868 	 * note that due to infelicities in the routing code, any default
869 	 * routes must be removed prior to clearing the UP flag.
870 	 */
871 
872 	remove_ifs_default_routes(ifsp);
873 
874 	/* LINTED [ifr_addr is a sockaddr which will be aligned] */
875 	sin = (struct sockaddr_in *)&ifr.ifr_addr;
876 
877 	(void) memset(&ifr, 0, sizeof (struct ifreq));
878 	(void) strlcpy(ifr.ifr_name, ifsp->if_name, IFNAMSIZ);
879 
880 	if (ioctl(ifsp->if_sock_fd, SIOCGIFFLAGS, &ifr) == -1)
881 		return (0);
882 
883 	/*
884 	 * clear the UP flag, but don't clear DHCPRUNNING since
885 	 * that should only be done when the interface is removed
886 	 * (see remove_ifs())
887 	 */
888 
889 	ifr.ifr_flags &= ~IFF_UP;
890 
891 	if (ioctl(ifsp->if_sock_fd, SIOCSIFFLAGS, &ifr) == -1)
892 		return (0);
893 
894 	/*
895 	 * since ifr is actually a union, we need to explicitly zero
896 	 * the flags field before we reuse the structure, or otherwise
897 	 * cruft may leak over into other members of the union.
898 	 */
899 
900 	ifr.ifr_flags = 0;
901 	ifr.ifr_addr.sa_family = AF_INET;
902 	sin->sin_addr.s_addr = htonl(INADDR_ANY);
903 
904 	if (ioctl(ifsp->if_sock_fd, SIOCSIFADDR, &ifr) == -1)
905 		return (0);
906 
907 	if (ioctl(ifsp->if_sock_fd, SIOCSIFNETMASK, &ifr) == -1)
908 		return (0);
909 
910 	if (ioctl(ifsp->if_sock_fd, SIOCSIFBRDADDR, &ifr) == -1)
911 		return (0);
912 
913 	/*
914 	 * any time we change the IP address, netmask, or broadcast we
915 	 * must be careful to also reset bookkeeping of what these are
916 	 * set to.  this is so we can detect if these characteristics
917 	 * are changed by another process.
918 	 */
919 
920 	ifsp->if_addr.s_addr	  = htonl(INADDR_ANY);
921 	ifsp->if_netmask.s_addr   = htonl(INADDR_ANY);
922 	ifsp->if_broadcast.s_addr = htonl(INADDR_ANY);
923 
924 	return (1);
925 }
926 
927 /*
928  * check_ifs(): makes sure an ifs is still valid, and if it is, releases the
929  *		ifs.  otherwise, it informs the caller the ifs is going away
930  *		and expects the caller to perform the release
931  *
932  *   input: struct ifslist *: the ifs to check
933  *  output: int: 1 if the interface is valid, 0 otherwise
934  */
935 
936 int
937 check_ifs(struct ifslist *ifsp)
938 {
939 	hold_ifs(ifsp);
940 	if (release_ifs(ifsp) == 1 || verify_ifs(ifsp) == 0) {
941 
942 		/*
943 		 * this interface is going away.  if there's an
944 		 * uncancelled IPC event roaming around, cancel it
945 		 * now.  we leave the hold on in case anyone else has
946 		 * any cleanup work that needs to be done before the
947 		 * interface goes away.
948 		 */
949 
950 		ipc_action_finish(ifsp, DHCP_IPC_E_UNKIF);
951 		async_finish(ifsp);
952 		return (0);
953 	}
954 
955 	(void) release_ifs(ifsp);
956 	return (1);
957 }
958 
959 /*
960  * nuke_ifslist(): delete the ifslist (for use when the dhcpagent is exiting)
961  *
962  *   input: boolean_t: B_TRUE if the agent is exiting due to SIGTERM
963  *  output: void
964  */
965 
966 void
967 nuke_ifslist(boolean_t onterm)
968 {
969 	int	status;
970 	struct ifslist	*ifsp, *ifsp_next;
971 
972 	for (ifsp = ifsheadp; ifsp != NULL; ifsp = ifsp_next) {
973 		ifsp_next = ifsp->next;
974 
975 		cancel_ifs_timers(ifsp);
976 		if (ifsp->if_script_pid != -1) {
977 			/* stop a script if it is not for DROP or RELEASE */
978 			if (strcmp(ifsp->if_script_event, EVENT_DROP) == 0 ||
979 			    strcmp(ifsp->if_script_event, EVENT_RELEASE) == 0) {
980 				continue;
981 			}
982 			script_stop(ifsp);
983 		}
984 
985 		/*
986 		 * if the script is started by script_start, dhcp_drop and
987 		 * dhcp_release should and will only be called after the
988 		 * script exits.
989 		 */
990 		if (onterm &&
991 		    df_get_bool(ifsp->if_name, DF_RELEASE_ON_SIGTERM)) {
992 			if (script_start(ifsp, EVENT_RELEASE, dhcp_release,
993 			    "DHCP agent is exiting", &status) == 1) {
994 				continue;
995 			}
996 			if (status == 1)
997 				continue;
998 		}
999 		(void) script_start(ifsp, EVENT_DROP, dhcp_drop, NULL, NULL);
1000 	}
1001 }
1002 
1003 /*
1004  * refresh_ifslist(): refreshes all finite leases under DHCP control
1005  *
1006  *   input: iu_eh_t *: unused
1007  *	    int: unused
1008  *	    void *: unused
1009  *  output: void
1010  */
1011 
1012 /* ARGSUSED */
1013 void
1014 refresh_ifslist(iu_eh_t *eh, int sig, void *arg)
1015 {
1016 	struct ifslist *ifsp;
1017 
1018 	for (ifsp = ifsheadp; ifsp != NULL; ifsp = ifsp->next) {
1019 
1020 		if (ifsp->if_state != BOUND && ifsp->if_state != RENEWING &&
1021 		    ifsp->if_state != REBINDING)
1022 			continue;
1023 
1024 		if (ifsp->if_lease == DHCP_PERM)
1025 			continue;
1026 
1027 		/*
1028 		 * this interface has a finite lease and we do not know
1029 		 * how long the machine's been off for.  refresh it.
1030 		 */
1031 
1032 		dhcpmsg(MSG_WARNING, "refreshing lease on %s", ifsp->if_name);
1033 		cancel_ifs_timer(ifsp, DHCP_T1_TIMER);
1034 		cancel_ifs_timer(ifsp, DHCP_T2_TIMER);
1035 		(void) iu_adjust_timer(tq, ifsp->if_timer[DHCP_LEASE_TIMER], 0);
1036 	}
1037 }
1038 
1039 /*
1040  * ifs_count(): returns the number of interfaces currently managed
1041  *
1042  *   input: void
1043  *  output: unsigned int: the number of interfaces currently managed
1044  */
1045 
1046 unsigned int
1047 ifs_count(void)
1048 {
1049 	return (ifscount);
1050 }
1051 
1052 /*
1053  * cancel_ifs_timer(): cancels a lease-related timer on an interface
1054  *
1055  *   input: struct ifslist *: the interface to operate on
1056  *	    int: the timer id of the timer to cancel
1057  *  output: void
1058  */
1059 
1060 static void
1061 cancel_ifs_timer(struct ifslist *ifsp, int timer_id)
1062 {
1063 	if (ifsp->if_timer[timer_id] != -1) {
1064 		if (iu_cancel_timer(tq, ifsp->if_timer[timer_id], NULL) == 1) {
1065 			(void) release_ifs(ifsp);
1066 			ifsp->if_timer[timer_id] = -1;
1067 		} else
1068 			dhcpmsg(MSG_WARNING, "cancel_ifs_timer: cannot cancel "
1069 			    "if_timer[%d]", timer_id);
1070 	}
1071 }
1072 
1073 /*
1074  * cancel_ifs_timers(): cancels an interface's pending lease-related timers
1075  *
1076  *   input: struct ifslist *: the interface to operate on
1077  *  output: void
1078  */
1079 
1080 void
1081 cancel_ifs_timers(struct ifslist *ifsp)
1082 {
1083 	cancel_ifs_timer(ifsp, DHCP_T1_TIMER);
1084 	cancel_ifs_timer(ifsp, DHCP_T2_TIMER);
1085 	cancel_ifs_timer(ifsp, DHCP_LEASE_TIMER);
1086 }
1087 
1088 /*
1089  * schedule_ifs_timer(): schedules a lease-related timer on an interface
1090  *
1091  *   input: struct ifslist *: the interface to operate on
1092  *	    int: the timer to schedule
1093  *	    uint32_t: the number of seconds in the future it should fire
1094  *	    iu_tq_callback_t *: the callback to call upon firing
1095  *  output: int: 1 if the timer was scheduled successfully, 0 on failure
1096  */
1097 
1098 int
1099 schedule_ifs_timer(struct ifslist *ifsp, int timer_id, uint32_t sec,
1100     iu_tq_callback_t *expire)
1101 {
1102 	cancel_ifs_timer(ifsp, timer_id);		/* just in case */
1103 
1104 	ifsp->if_timer[timer_id] = iu_schedule_timer(tq, sec, expire, ifsp);
1105 	if (ifsp->if_timer[timer_id] == -1) {
1106 		dhcpmsg(MSG_WARNING, "schedule_ifs_timer: cannot schedule "
1107 		    "if_timer[%d]", timer_id);
1108 		return (0);
1109 	}
1110 
1111 	hold_ifs(ifsp);
1112 	return (1);
1113 }
1114 
1115 /*
1116  * Get the value of the named property on the named node in devinfo root.
1117  *
1118  *   input: const char *: The name of the node containing the property.
1119  *	    const char *: The name of the property.
1120  *	    uchar_t **: The property value, modified iff B_TRUE is returned.
1121  *                      If no value is found the value is set to NULL.
1122  *	    unsigned int *: The length of the property value
1123  *  output: boolean_t: Returns B_TRUE if successful (no problems),
1124  *                     otherwise B_FALSE.
1125  *    note: The memory allocated by this function must be freed by
1126  *          the caller. This code is derived from
1127  *          usr/src/lib/libwanboot/common/bootinfo_aux.c.
1128  */
1129 
1130 static boolean_t
1131 get_prom_prop(const char *nodename, const char *propname, uchar_t **propvaluep,
1132     unsigned int *lenp)
1133 {
1134 	di_node_t		root_node = DI_NODE_NIL;
1135 	di_node_t		node;
1136 	di_prom_handle_t	phdl = DI_PROM_HANDLE_NIL;
1137 	di_prom_prop_t		pp;
1138 	uchar_t			*value = NULL;
1139 	unsigned int		len = 0;
1140 	boolean_t		success = B_TRUE;
1141 
1142 	/*
1143 	 * locate root node
1144 	 */
1145 
1146 	if ((root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL ||
1147 	    (phdl = di_prom_init()) == DI_PROM_HANDLE_NIL) {
1148 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property root node "
1149 		    "not found");
1150 		goto get_prom_prop_cleanup;
1151 	}
1152 
1153 	/*
1154 	 * locate nodename within '/'
1155 	 */
1156 
1157 	for (node = di_child_node(root_node);
1158 	    node != DI_NODE_NIL;
1159 	    node = di_sibling_node(node)) {
1160 		if (strcmp(di_node_name(node), nodename) == 0) {
1161 			break;
1162 		}
1163 	}
1164 
1165 	if (node == DI_NODE_NIL) {
1166 		dhcpmsg(MSG_DEBUG, "get_prom_prop: node not found");
1167 		goto get_prom_prop_cleanup;
1168 	}
1169 
1170 	/*
1171 	 * scan all properties of /nodename for the 'propname' property
1172 	 */
1173 
1174 	for (pp = di_prom_prop_next(phdl, node, DI_PROM_PROP_NIL);
1175 	    pp != DI_PROM_PROP_NIL;
1176 	    pp = di_prom_prop_next(phdl, node, pp)) {
1177 
1178 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property = %s",
1179 		    di_prom_prop_name(pp));
1180 
1181 		if (strcmp(propname, di_prom_prop_name(pp)) == 0) {
1182 			break;
1183 		}
1184 	}
1185 
1186 	if (pp == DI_PROM_PROP_NIL) {
1187 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property not found");
1188 		goto get_prom_prop_cleanup;
1189 	}
1190 
1191 	/*
1192 	 * get the property; allocate some memory copy it out
1193 	 */
1194 
1195 	len = di_prom_prop_data(pp, (uchar_t **)&value);
1196 
1197 	if (value == NULL) {
1198 		/*
1199 		 * property data read problems
1200 		 */
1201 
1202 		success = B_FALSE;
1203 		dhcpmsg(MSG_ERR, "get_prom_prop: cannot read property data");
1204 		goto get_prom_prop_cleanup;
1205 	}
1206 
1207 	if (propvaluep != NULL) {
1208 		/*
1209 		 * allocate somewhere to copy the property value to
1210 		 */
1211 
1212 		*propvaluep = calloc(len, sizeof (uchar_t));
1213 
1214 		if (*propvaluep == NULL) {
1215 			/*
1216 			 * allocation problems
1217 			 */
1218 
1219 			success = B_FALSE;
1220 			dhcpmsg(MSG_ERR, "get_prom_prop: cannot allocate "
1221 			    "memory for property value");
1222 			goto get_prom_prop_cleanup;
1223 		}
1224 
1225 		/*
1226 		 * copy data out
1227 		 */
1228 
1229 		(void) memcpy(*propvaluep, value, len);
1230 
1231 		/*
1232 		 * copy out the length if a suitable pointer has
1233 		 * been supplied
1234 		 */
1235 
1236 		if (lenp != NULL) {
1237 			*lenp = len;
1238 		}
1239 
1240 		dhcpmsg(MSG_DEBUG, "get_prom_prop: property value "
1241 		    "length = %d", len);
1242 	}
1243 
1244 get_prom_prop_cleanup:
1245 
1246 	if (phdl != DI_PROM_HANDLE_NIL) {
1247 		di_prom_fini(phdl);
1248 	}
1249 
1250 	if (root_node != DI_NODE_NIL) {
1251 		di_fini(root_node);
1252 	}
1253 
1254 	return (success);
1255 }
1256