xref: /titanic_50/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c (revision 1f4643f9130cd5a36ddbe698eb88302e44570813)
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 2007 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 <search.h>
39 #include <libdevinfo.h>
40 #include <netinet/if_ether.h>
41 #include <arpa/inet.h>
42 #include <dhcpmsg.h>
43 #include <dhcp_inittab.h>
44 
45 #include "agent.h"
46 #include "interface.h"
47 #include "util.h"
48 #include "dlpi_io.h"
49 #include "packet.h"
50 #include "states.h"
51 
52 dhcp_pif_t *v4root;
53 dhcp_pif_t *v6root;
54 
55 static uint_t cached_v4_max_mtu, cached_v6_max_mtu;
56 
57 /*
58  * Interface flags to watch: things that should be under our direct control.
59  */
60 #define	DHCP_IFF_WATCH	(IFF_DHCPRUNNING | IFF_DEPRECATED | \
61 	IFF_ADDRCONF | IFF_MIPRUNNING | IFF_TEMPORARY)
62 
63 static void clear_lif_dhcp(dhcp_lif_t *);
64 
65 /*
66  * insert_pif(): creates a new physical interface structure and chains it on
67  *		 the list.  Initializes state that remains consistent across
68  *		 all use of the physical interface entry.
69  *
70  *   input: const char *: the name of the physical interface
71  *	    boolean_t: if B_TRUE, this is DHCPv6
72  *	    int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
73  *		   error code with the reason why
74  *  output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure
75  */
76 
77 dhcp_pif_t *
78 insert_pif(const char *pname, boolean_t isv6, int *error)
79 {
80 	dhcp_pif_t *pif;
81 	struct lifreq lifr;
82 
83 	if ((pif = calloc(1, sizeof (*pif))) == NULL) {
84 		dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for "
85 		    "%s", pname);
86 		*error = DHCP_IPC_E_MEMORY;
87 		return (NULL);
88 	}
89 
90 	pif->pif_isv6 = isv6;
91 	pif->pif_dlpi_fd = -1;
92 	pif->pif_dlpi_id = -1;
93 	pif->pif_hold_count = 1;
94 	pif->pif_running = B_TRUE;
95 
96 	if (strlcpy(pif->pif_name, pname, LIFNAMSIZ) >= LIFNAMSIZ) {
97 		dhcpmsg(MSG_ERROR, "insert_pif: interface name %s is too long",
98 		    pname);
99 		*error = DHCP_IPC_E_INVIF;
100 		goto failure;
101 	}
102 
103 	/* We do not use DLPI with DHCPv6 */
104 	if (!isv6) {
105 		uint32_t		buf[DLPI_BUF_MAX / sizeof (uint32_t)];
106 		dl_info_ack_t		*dlia = (dl_info_ack_t *)buf;
107 		caddr_t			dl_addr;
108 
109 		/*
110 		 * Do the allocations necessary for IPv4 DHCP.
111 		 *
112 		 *  1. open the interface using DLPI
113 		 *  2. get the interface max SDU
114 		 *  3. get the interface hardware type and hardware length
115 		 *  4. get the interface hardware address
116 		 *  5. get the interface hardware broadcast address
117 		 */
118 
119 		/* step 1 */
120 		pif->pif_dlpi_fd = dhcp_dlpi_open(pname, dlia, sizeof (buf),
121 		    ETHERTYPE_IP);
122 		if (pif->pif_dlpi_fd == -1) {
123 			*error = DHCP_IPC_E_INVIF;
124 			goto failure;
125 		}
126 
127 		/* step 2 */
128 		pif->pif_max = dlia->dl_max_sdu;
129 		if (pif->pif_max < DHCP_DEF_MAX_SIZE) {
130 			dhcpmsg(MSG_ERROR, "insert_pif: %s does not have a "
131 			    "large enough maximum SDU to support DHCP "
132 			    "(%u < %u)", pname, pif->pif_max,
133 			    DHCP_DEF_MAX_SIZE);
134 			*error = DHCP_IPC_E_INVIF;
135 			goto failure;
136 		}
137 
138 		/* step 3 */
139 		pif->pif_hwtype = dlpi_to_arp(dlia->dl_mac_type);
140 		pif->pif_hwlen  = dlia->dl_addr_length -
141 		    abs(dlia->dl_sap_length);
142 
143 		dhcpmsg(MSG_DEBUG, "insert_pif: %s: sdumax %u, hwtype %d, "
144 		    "hwlen %d", pname, pif->pif_max, pif->pif_hwtype,
145 		    pif->pif_hwlen);
146 
147 		/* step 4 */
148 		if (pif->pif_hwlen > 0) {
149 			pif->pif_hwaddr = malloc(pif->pif_hwlen);
150 			if (pif->pif_hwaddr == NULL) {
151 				dhcpmsg(MSG_ERR, "insert_pif: cannot allocate "
152 				    "pif_hwaddr for %s", pname);
153 				*error = DHCP_IPC_E_MEMORY;
154 				goto failure;
155 			}
156 		}
157 
158 		/*
159 		 * depending on the DLPI device, the sap and hardware addresses
160 		 * can be in either order within the dlsap address; find the
161 		 * location of the hardware address using dl_sap_length.  see
162 		 * the DLPI specification for more on this braindamage.
163 		 */
164 
165 		dl_addr = (caddr_t)dlia + dlia->dl_addr_offset;
166 		if (dlia->dl_sap_length > 0) {
167 			pif->pif_sap_before = B_TRUE;
168 			dl_addr += dlia->dl_sap_length;
169 		}
170 
171 		(void) memcpy(pif->pif_hwaddr, dl_addr, pif->pif_hwlen);
172 
173 		/* step 5 */
174 		pif->pif_saplen = abs(dlia->dl_sap_length);
175 		pif->pif_daddr  = build_broadcast_dest(dlia, &pif->pif_dlen);
176 		if (pif->pif_daddr == NULL) {
177 			dhcpmsg(MSG_ERR, "insert_pif: cannot allocate "
178 			    "pif_daddr for %s", pname);
179 			*error = DHCP_IPC_E_MEMORY;
180 			goto failure;
181 		}
182 
183 		/* Close the DLPI stream until actually needed */
184 		close_dlpi_pif(pif);
185 	}
186 
187 	/*
188 	 * This is a bit gross, but IP has a confused interface.  We must
189 	 * assume that the zeroth LIF is plumbed, and must query there to get
190 	 * the interface index number.
191 	 */
192 	(void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ);
193 
194 	if (ioctl(isv6 ? v6_sock_fd : v4_sock_fd, SIOCGLIFINDEX, &lifr) == -1) {
195 		if (errno == ENXIO)
196 			*error = DHCP_IPC_E_INVIF;
197 		else
198 			*error = DHCP_IPC_E_INT;
199 		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname);
200 		goto failure;
201 	}
202 	pif->pif_index = lifr.lifr_index;
203 
204 	insque(pif, isv6 ? &v6root : &v4root);
205 
206 	return (pif);
207 
208 failure:
209 	release_pif(pif);
210 	return (NULL);
211 }
212 
213 /*
214  * hold_pif(): acquire a hold on a physical interface structure.
215  *
216  *   input: dhcp_pif_t *: a pointer to the PIF structure
217  *  output: none
218  */
219 
220 void
221 hold_pif(dhcp_pif_t *pif)
222 {
223 	pif->pif_hold_count++;
224 	dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name,
225 	    pif->pif_hold_count);
226 }
227 
228 /*
229  * release_pif(): release a hold on a physical interface structure; will
230  *		  destroy the structure on the last hold removed.
231  *
232  *   input: dhcp_pif_t *: a pointer to the PIF structure
233  *  output: none
234  */
235 
236 void
237 release_pif(dhcp_pif_t *pif)
238 {
239 	if (pif->pif_hold_count == 0) {
240 		dhcpmsg(MSG_CRIT, "release_pif: extraneous release");
241 		return;
242 	}
243 
244 	if (--pif->pif_hold_count == 0) {
245 		dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s",
246 		    pif->pif_name);
247 
248 		remque(pif);
249 		pif->pif_dlpi_count = 1;
250 		close_dlpi_pif(pif);
251 		free(pif->pif_hwaddr);
252 		free(pif->pif_daddr);
253 		free(pif);
254 	} else {
255 		dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u",
256 		    pif->pif_name, pif->pif_hold_count);
257 	}
258 }
259 
260 /*
261  * lookup_pif_by_index(): Looks up PIF entries given regular ifIndex.
262  *
263  *   input: uint_t: the interface index
264  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
265  *  output: dhcp_pif_t *: the matching PIF, or NULL if not found
266  */
267 
268 dhcp_pif_t *
269 lookup_pif_by_index(uint_t ifindex, boolean_t isv6)
270 {
271 	dhcp_pif_t *pif;
272 
273 	for (pif = isv6 ? v6root : v4root; pif != NULL; pif = pif->pif_next) {
274 		if (pif->pif_index == ifindex)
275 			break;
276 	}
277 
278 	return (pif);
279 }
280 
281 /*
282  * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and
283  *			   previous PIF pointer (or NULL for list start).
284  *			   Caller is expected to iterate through all
285  *			   potential matches to find interface of interest.
286  *
287  *   input: uint16_t: the interface index (truncated)
288  *	    dhcp_pif_t *: the previous PIF, or NULL for list start
289  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
290  *  output: dhcp_pif_t *: the next matching PIF, or NULL if not found
291  *    note: This operates using the 'truncated' (16-bit) ifindex as seen by
292  *	    routing socket clients.  The value stored in pif_index is the
293  *	    32-bit ifindex from the ioctl interface.
294  */
295 
296 dhcp_pif_t *
297 lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6)
298 {
299 	if (pif == NULL)
300 		pif = isv6 ? v6root : v4root;
301 	else
302 		pif = pif->pif_next;
303 
304 	for (; pif != NULL; pif = pif->pif_next) {
305 		if ((pif->pif_index & 0xffff) == ifindex)
306 			break;
307 	}
308 
309 	return (pif);
310 }
311 
312 /*
313  * lookup_pif_by_name(): Looks up a physical interface entry given a name.
314  *
315  *   input: const char *: the physical interface name
316  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
317  *  output: dhcp_pif_t *: the matching PIF, or NULL if not found
318  */
319 
320 dhcp_pif_t *
321 lookup_pif_by_name(const char *pname, boolean_t isv6)
322 {
323 	dhcp_pif_t *pif;
324 
325 	pif = isv6 ? v6root : v4root;
326 
327 	for (; pif != NULL; pif = pif->pif_next) {
328 		if (strcmp(pif->pif_name, pname) == 0)
329 			break;
330 	}
331 
332 	return (pif);
333 }
334 
335 /*
336  * open_dlpi_pif(): register the use of DLPI I/O by a LIF on a PIF, opening
337  *		    the connection if necessary.
338  *
339  *   input: dhcp_pif_t *: the physical interface on which to use DLPI
340  *  output: boolean_t: B_TRUE on success, B_FALSE on failure.
341  */
342 
343 boolean_t
344 open_dlpi_pif(dhcp_pif_t *pif)
345 {
346 	if (pif->pif_dlpi_fd == -1) {
347 		uint32_t		buf[DLPI_BUF_MAX / sizeof (uint32_t)];
348 		dl_info_ack_t		*dlia = (dl_info_ack_t *)buf;
349 
350 		pif->pif_dlpi_fd = dhcp_dlpi_open(pif->pif_name, dlia,
351 		    sizeof (buf), ETHERTYPE_IP);
352 		if (pif->pif_dlpi_fd == -1)
353 			return (B_FALSE);
354 		set_packet_filter(pif->pif_dlpi_fd, dhcp_filter, NULL, "DHCP");
355 		pif->pif_dlpi_id = iu_register_event(eh, pif->pif_dlpi_fd,
356 		    POLLIN, dhcp_collect_dlpi, pif);
357 		if (pif->pif_dlpi_id == -1) {
358 			(void) dhcp_dlpi_close(pif->pif_dlpi_fd);
359 			pif->pif_dlpi_fd = -1;
360 			return (B_FALSE);
361 		}
362 	}
363 	pif->pif_dlpi_count++;
364 	return (B_TRUE);
365 }
366 
367 /*
368  * close_dlpi_pif(): unregister the use of DLPI I/O by a LIF on a PIF, closing
369  *		     the connection if this was the last user.
370  *
371  *   input: dhcp_pif_t *: the physical interface on which we're using DLPI
372  *  output: none
373  */
374 
375 void
376 close_dlpi_pif(dhcp_pif_t *pif)
377 {
378 	if (pif->pif_dlpi_count > 1) {
379 		pif->pif_dlpi_count--;
380 		return;
381 	}
382 	pif->pif_dlpi_count = 0;
383 	if (pif->pif_dlpi_id != -1) {
384 		(void) iu_unregister_event(eh, pif->pif_dlpi_id, NULL);
385 		pif->pif_dlpi_id = -1;
386 	}
387 	if (pif->pif_dlpi_fd != -1) {
388 		(void) dhcp_dlpi_close(pif->pif_dlpi_fd);
389 		pif->pif_dlpi_fd = -1;
390 	}
391 }
392 
393 /*
394  * pif_status(): update the physical interface up/down status.
395  *
396  *   input: dhcp_pif_t *: the physical interface on which we're using DLPI
397  *	    boolean_t: B_TRUE if the interface is going up
398  *  output: none
399  */
400 
401 void
402 pif_status(dhcp_pif_t *pif, boolean_t isup)
403 {
404 	dhcp_lif_t *lif;
405 	dhcp_smach_t *dsmp;
406 
407 	pif->pif_running = isup;
408 	dhcpmsg(LOG_DEBUG, "interface %s has %s", pif->pif_name,
409 	    isup ? "come back up" : "gone down");
410 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
411 		for (dsmp = lif->lif_smachs; dsmp != NULL;
412 		    dsmp = dsmp->dsm_next) {
413 			if (isup)
414 				refresh_smach(dsmp);
415 			else
416 				remove_default_routes(dsmp);
417 		}
418 	}
419 }
420 
421 /* Helper for insert_lif: extract addresses as defined */
422 #define	ASSIGN_ADDR(v4, v6, lf) \
423 	if (pif->pif_isv6) { \
424 		lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \
425 	} else { \
426 		lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \
427 	}
428 
429 /*
430  * insert_lif(): Creates a new logical interface structure and chains it on
431  *		 the list for a given physical interface.  Initializes state
432  *		 that remains consistent across all use of the logical
433  *		 interface entry.  Caller's PIF hold is transferred to the
434  *		 LIF on success, and is dropped on failure.
435  *
436  *   input: dhcp_pif_t *: pointer to the physical interface for this LIF
437  *	    const char *: the name of the logical interface
438  *	    int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
439  *		   error code with the reason why
440  *  output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure
441  */
442 
443 dhcp_lif_t *
444 insert_lif(dhcp_pif_t *pif, const char *lname, int *error)
445 {
446 	dhcp_lif_t *lif;
447 	int fd;
448 	struct lifreq lifr;
449 
450 	if ((lif = calloc(1, sizeof (*lif))) == NULL) {
451 		dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for "
452 		    "%s", lname);
453 		*error = DHCP_IPC_E_MEMORY;
454 		return (NULL);
455 	}
456 
457 	lif->lif_sock_ip_fd = -1;
458 	lif->lif_acknak_id = -1;
459 	lif->lif_iaid_id = -1;
460 	lif->lif_hold_count = 1;
461 	lif->lif_pif = pif;
462 	lif->lif_removed = B_TRUE;
463 	init_timer(&lif->lif_preferred, 0);
464 	init_timer(&lif->lif_expire, 0);
465 
466 	if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) {
467 		dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long",
468 		    lname);
469 		*error = DHCP_IPC_E_INVIF;
470 		goto failure;
471 	}
472 
473 	(void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ);
474 
475 	fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
476 
477 	if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1)
478 		lif->lif_max = 1024;
479 	else
480 		lif->lif_max = lifr.lifr_mtu;
481 
482 	if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) {
483 		if (errno == ENXIO)
484 			*error = DHCP_IPC_E_INVIF;
485 		else
486 			*error = DHCP_IPC_E_INT;
487 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname);
488 		goto failure;
489 	}
490 	ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr);
491 
492 	if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) {
493 		if (errno == ENXIO)
494 			*error = DHCP_IPC_E_INVIF;
495 		else
496 			*error = DHCP_IPC_E_INT;
497 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname);
498 		goto failure;
499 	}
500 	ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr);
501 
502 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
503 		*error = DHCP_IPC_E_INT;
504 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname);
505 		goto failure;
506 	}
507 	lif->lif_flags = lifr.lifr_flags;
508 
509 	/*
510 	 * If we've just detected the interface going up or down, then signal
511 	 * an appropriate action.  There may be other state machines here.
512 	 */
513 	if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) {
514 		pif_status(pif, B_TRUE);
515 	} else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) {
516 		pif_status(pif, B_FALSE);
517 	}
518 
519 	if (lifr.lifr_flags & IFF_POINTOPOINT) {
520 		if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) {
521 			*error = DHCP_IPC_E_INT;
522 			dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s",
523 			    lname);
524 			goto failure;
525 		}
526 		ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr);
527 	} else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) {
528 		if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) {
529 			*error = DHCP_IPC_E_INT;
530 			dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s",
531 			    lname);
532 			goto failure;
533 		}
534 		lif->lif_broadcast =
535 		    ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr.
536 		    s_addr;
537 	}
538 
539 	if (pif->pif_isv6)
540 		cached_v6_max_mtu = 0;
541 	else
542 		cached_v4_max_mtu = 0;
543 
544 	lif->lif_removed = B_FALSE;
545 	insque(lif, &pif->pif_lifs);
546 
547 	return (lif);
548 
549 failure:
550 	release_lif(lif);
551 	return (NULL);
552 }
553 
554 /*
555  * hold_lif(): acquire a hold on a logical interface structure.
556  *
557  *   input: dhcp_lif_t *: a pointer to the LIF structure
558  *  output: none
559  */
560 
561 void
562 hold_lif(dhcp_lif_t *lif)
563 {
564 	lif->lif_hold_count++;
565 	dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name,
566 	    lif->lif_hold_count);
567 }
568 
569 /*
570  * release_lif(): release a hold on a logical interface structure; will
571  *		  destroy the structure on the last hold removed.
572  *
573  *   input: dhcp_lif_t *: a pointer to the LIF structure
574  *  output: none
575  */
576 
577 void
578 release_lif(dhcp_lif_t *lif)
579 {
580 	if (lif->lif_hold_count == 0) {
581 		dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s",
582 		    lif->lif_name);
583 		return;
584 	}
585 
586 	if (lif->lif_hold_count == 1 && !lif->lif_removed) {
587 		unplumb_lif(lif);
588 		return;
589 	}
590 
591 	if (--lif->lif_hold_count == 0) {
592 		dhcp_pif_t *pif;
593 
594 		dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s",
595 		    lif->lif_name);
596 
597 		if (lif->lif_lease != NULL)
598 			dhcpmsg(MSG_CRIT,
599 			    "release_lif: still holding lease at last hold!");
600 		close_ip_lif(lif);
601 		pif = lif->lif_pif;
602 		if (pif->pif_isv6)
603 			cached_v6_max_mtu = 0;
604 		else
605 			cached_v4_max_mtu = 0;
606 		release_pif(pif);
607 		free(lif);
608 	} else {
609 		dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u",
610 		    lif->lif_name, lif->lif_hold_count);
611 	}
612 }
613 
614 /*
615  * remove_lif(): remove a logical interface from its PIF and lease (if any) and
616  *		 the lease's hold on the LIF.  Assumes that we did not plumb
617  *		 the interface.
618  *
619  *   input: dhcp_lif_t *: a pointer to the LIF structure
620  *  output: none
621  */
622 
623 void
624 remove_lif(dhcp_lif_t *lif)
625 {
626 	if (lif->lif_plumbed) {
627 		dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s",
628 		    lif->lif_name);
629 		return;
630 	}
631 	if (lif->lif_removed) {
632 		dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s",
633 		    lif->lif_name);
634 	} else {
635 		dhcp_lif_t *lifnext;
636 		dhcp_lease_t *dlp;
637 
638 		dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name);
639 		lif->lif_removed = B_TRUE;
640 		lifnext = lif->lif_next;
641 		clear_lif_dhcp(lif);
642 		cancel_lif_timers(lif);
643 		if (lif->lif_iaid_id != -1 &&
644 		    iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) {
645 			lif->lif_iaid_id = -1;
646 			release_lif(lif);
647 		}
648 
649 		/* Remove from PIF list */
650 		remque(lif);
651 
652 		/* If we were part of a lease, then remove ourselves */
653 		if ((dlp = lif->lif_lease) != NULL) {
654 			if (--dlp->dl_nlifs == 0)
655 				dlp->dl_lifs = NULL;
656 			else if (dlp->dl_lifs == lif)
657 				dlp->dl_lifs = lifnext;
658 			if (lif->lif_flags & IFF_DHCPRUNNING)
659 				clear_lif_dhcp(lif);
660 			if (lif->lif_declined != NULL) {
661 				dlp->dl_smach->dsm_lif_down--;
662 				lif->lif_declined = NULL;
663 			}
664 			lif->lif_lease = NULL;
665 			release_lif(lif);
666 		}
667 	}
668 }
669 
670 /*
671  * lookup_lif_by_name(): Looks up a logical interface entry given a name and
672  *			 a physical interface.
673  *
674  *   input: const char *: the logical interface name
675  *	    const dhcp_pif_t *: the physical interface
676  *  output: dhcp_lif_t *: the matching LIF, or NULL if not found
677  */
678 
679 dhcp_lif_t *
680 lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif)
681 {
682 	dhcp_lif_t *lif;
683 
684 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
685 		if (strcmp(lif->lif_name, lname) == 0)
686 			break;
687 	}
688 
689 	return (lif);
690 }
691 
692 /*
693  * checkaddr(): checks if the given address is still set on the given LIF
694  *
695  *   input: const dhcp_lif_t *: the LIF to check
696  *	    int: the address to look up on the interface (ioctl)
697  *	    const in6_addr_t *: the address to compare to
698  *	    const char *: name of the address for logging purposes
699  *  output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
700  */
701 
702 static boolean_t
703 checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr,
704     const char *aname)
705 {
706 	boolean_t isv6;
707 	int fd;
708 	struct lifreq lifr;
709 
710 	(void) memset(&lifr, 0, sizeof (struct lifreq));
711 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
712 
713 	isv6 = lif->lif_pif->pif_isv6;
714 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
715 
716 	if (ioctl(fd, ioccmd, &lifr) == -1) {
717 		if (errno == ENXIO) {
718 			dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone",
719 			    lif->lif_name);
720 			return (B_FALSE);
721 		}
722 		dhcpmsg(MSG_DEBUG,
723 		    "checkaddr: ignoring ioctl error on %s %x: %s",
724 		    lif->lif_name, ioccmd, strerror(errno));
725 	} else if (isv6) {
726 		struct sockaddr_in6 *sin6 =
727 		    (struct sockaddr_in6 *)&lifr.lifr_addr;
728 		char abuf1[INET6_ADDRSTRLEN];
729 		char abuf2[INET6_ADDRSTRLEN];
730 
731 		if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) {
732 			dhcpmsg(MSG_WARNING,
733 			    "checkaddr: expected %s %s on %s, have %s",
734 			    aname, inet_ntop(AF_INET6, &sin6->sin6_addr, abuf1,
735 			    sizeof (abuf1)),  lif->lif_name,
736 			    inet_ntop(AF_INET6, addr, abuf2, sizeof (abuf2)));
737 			return (B_FALSE);
738 		}
739 	} else {
740 		struct sockaddr_in *sinp =
741 		    (struct sockaddr_in *)&lifr.lifr_addr;
742 		ipaddr_t v4addr;
743 		char abuf1[INET_ADDRSTRLEN];
744 		char abuf2[INET_ADDRSTRLEN];
745 
746 		IN6_V4MAPPED_TO_IPADDR(addr, v4addr);
747 		if (sinp->sin_addr.s_addr != v4addr) {
748 			dhcpmsg(MSG_WARNING,
749 			    "checkaddr: expected %s %s on %s, have %s",
750 			    aname, inet_ntop(AF_INET, &sinp->sin_addr, abuf1,
751 			    sizeof (abuf1)),  lif->lif_name,
752 			    inet_ntop(AF_INET, &v4addr, abuf2,
753 			    sizeof (abuf2)));
754 			return (B_FALSE);
755 		}
756 	}
757 	return (B_TRUE);
758 }
759 
760 /*
761  * verify_lif(): verifies than a LIF is still valid (i.e., has not been
762  *		 explicitly or implicitly dropped or released)
763  *
764  *   input: const dhcp_lif_t *: the LIF to verify
765  *  output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise
766  */
767 
768 boolean_t
769 verify_lif(const dhcp_lif_t *lif)
770 {
771 	boolean_t isv6;
772 	int fd;
773 	struct lifreq lifr;
774 
775 	(void) memset(&lifr, 0, sizeof (struct lifreq));
776 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
777 
778 	isv6 = lif->lif_pif->pif_isv6;
779 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
780 
781 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
782 		if (errno != ENXIO) {
783 			dhcpmsg(MSG_ERR,
784 			    "verify_lif: SIOCGLIFFLAGS failed on %s",
785 			    lif->lif_name);
786 		}
787 		return (B_FALSE);
788 	}
789 
790 	/*
791 	 * If important flags have changed, then abandon the interface.
792 	 */
793 	if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) {
794 		dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: "
795 		    "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags,
796 		    lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) &
797 		    DHCP_IFF_WATCH);
798 		return (B_FALSE);
799 	}
800 
801 	/*
802 	 * Special case: if the interface has gone down as a duplicate, then
803 	 * this alone does _not_ mean that we're abandoning it just yet.  Allow
804 	 * the state machine to handle this normally by trying to get a new
805 	 * lease.
806 	 */
807 	if ((lifr.lifr_flags & (IFF_UP|IFF_DUPLICATE)) == IFF_DUPLICATE) {
808 		dhcpmsg(MSG_DEBUG, "verify_lif: duplicate address on %s",
809 		    lif->lif_name);
810 		return (B_TRUE);
811 	}
812 
813 	/*
814 	 * If the user has torn down or started up the interface manually, then
815 	 * abandon the lease.
816 	 */
817 	if ((lif->lif_flags ^ lifr.lifr_flags) & IFF_UP) {
818 		dhcpmsg(MSG_DEBUG, "verify_lif: user has %s %s",
819 		    lifr.lifr_flags & IFF_UP ? "started up" : "shut down",
820 		    lif->lif_name);
821 		return (B_FALSE);
822 	}
823 
824 	/*
825 	 * Check for delete and recreate.
826 	 */
827 	if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
828 		dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed on %s",
829 		    lif->lif_name);
830 		return (B_FALSE);
831 	}
832 	if (lifr.lifr_index != lif->lif_pif->pif_index) {
833 		dhcpmsg(MSG_DEBUG,
834 		    "verify_lif: ifindex on %s changed: %u to %u",
835 		    lif->lif_name, lif->lif_pif->pif_index, lifr.lifr_index);
836 		return (B_FALSE);
837 	}
838 
839 	/*
840 	 * If the IP address, netmask, or broadcast address have changed, or
841 	 * the interface has been unplumbed, then we act like there has been an
842 	 * implicit drop.  (Note that the netmask is under DHCP control for
843 	 * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast
844 	 * addresses.)
845 	 */
846 
847 	if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address"))
848 		return (B_FALSE);
849 
850 	if (isv6) {
851 		/*
852 		 * If it's not point-to-point, we're done.  If it is, then
853 		 * check the peer's address as well.
854 		 */
855 		return (!(lif->lif_flags & IFF_POINTOPOINT) ||
856 		    checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer,
857 		    "peer address"));
858 	} else {
859 		if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask,
860 		    "netmask"))
861 			return (B_FALSE);
862 
863 		return (checkaddr(lif,
864 		    (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR :
865 		    SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address"));
866 	}
867 }
868 
869 /*
870  * canonize_lif(): puts the interface in a canonical (zeroed) form.  This is
871  *		   used only on the "main" LIF for IPv4.  All other interfaces
872  *		   are under dhcpagent control and are removed using
873  *		   unplumb_lif().
874  *
875  *   input: dhcp_lif_t *: the interface to canonize
876  *  output: none
877  */
878 
879 static void
880 canonize_lif(dhcp_lif_t *lif)
881 {
882 	boolean_t isv6;
883 	int fd;
884 	struct lifreq lifr;
885 
886 	/*
887 	 * If there's nothing here, then don't touch the interface.  This can
888 	 * happen when an already-canonized LIF is recanonized.
889 	 */
890 	if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr))
891 		return;
892 
893 	isv6 = lif->lif_pif->pif_isv6;
894 	dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s",
895 	    isv6 ? 6 : 4, lif->lif_name);
896 
897 	lif->lif_v6addr = my_in6addr_any;
898 	lif->lif_v6mask = my_in6addr_any;
899 	lif->lif_v6peer = my_in6addr_any;
900 
901 	(void) memset(&lifr, 0, sizeof (struct lifreq));
902 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
903 
904 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
905 
906 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
907 		if (errno != ENXIO) {
908 			dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s",
909 			    lif->lif_name);
910 		}
911 		return;
912 	}
913 
914 	/* Should not happen */
915 	if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) {
916 		dhcpmsg(MSG_INFO,
917 		    "canonize_lif: cannot clear %s; flags are %llx",
918 		    lif->lif_name, lifr.lifr_flags);
919 		return;
920 	}
921 
922 	/*
923 	 * clear the UP flag, but don't clear DHCPRUNNING since
924 	 * that should only be done when the interface is removed
925 	 * (see clear_lif_dhcp() and remove_lif())
926 	 */
927 
928 	lif->lif_flags = lifr.lifr_flags &= ~IFF_UP;
929 
930 	if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
931 		dhcpmsg(MSG_ERR, "canonize_lif: can't set flags for %s",
932 		    lif->lif_name);
933 		return;
934 	}
935 
936 	(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
937 	if (isv6) {
938 		struct sockaddr_in6 *sin6 =
939 		    (struct sockaddr_in6 *)&lifr.lifr_addr;
940 
941 		sin6->sin6_family = AF_INET6;
942 		sin6->sin6_addr = my_in6addr_any;
943 	} else {
944 		struct sockaddr_in *sinv =
945 		    (struct sockaddr_in *)&lifr.lifr_addr;
946 
947 		sinv->sin_family = AF_INET;
948 		sinv->sin_addr.s_addr = htonl(INADDR_ANY);
949 	}
950 
951 	if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) {
952 		dhcpmsg(MSG_ERR,
953 		    "canonize_lif: can't clear local address on %s",
954 		    lif->lif_name);
955 	}
956 
957 	/* Netmask is under in.ndpd control with IPv6 */
958 	if (!isv6 && ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) {
959 		dhcpmsg(MSG_ERR, "canonize_lif: can't clear netmask on %s",
960 		    lif->lif_name);
961 	}
962 
963 	if (lif->lif_flags & IFF_POINTOPOINT) {
964 		if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) {
965 			dhcpmsg(MSG_ERR,
966 			    "canonize_lif: can't clear remote address on %s",
967 			    lif->lif_name);
968 		}
969 	} else if (!isv6) {
970 		if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) {
971 			dhcpmsg(MSG_ERR,
972 			    "canonize_lif: can't clear broadcast address on %s",
973 			    lif->lif_name);
974 		}
975 	}
976 }
977 
978 /*
979  * plumb_lif(): Adds the LIF to the system.  This is used for all
980  *		DHCPv6-derived interfaces.  The returned LIF has a hold
981  *		on it.
982  *
983  *   input: dhcp_lif_t *: the interface to unplumb
984  *  output: none
985  */
986 
987 dhcp_lif_t *
988 plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr)
989 {
990 	dhcp_lif_t *lif;
991 	char abuf[INET6_ADDRSTRLEN];
992 	struct lifreq lifr;
993 	struct sockaddr_in6 *sin6;
994 	int error;
995 
996 	(void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf));
997 
998 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
999 		if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) {
1000 			dhcpmsg(MSG_ERR,
1001 			    "plumb_lif: entry for %s already exists!", abuf);
1002 			return (NULL);
1003 		}
1004 	}
1005 
1006 	/* First, create a new zero-address logical interface */
1007 	(void) memset(&lifr, 0, sizeof (lifr));
1008 	(void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name));
1009 	if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) {
1010 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name);
1011 		return (NULL);
1012 	}
1013 
1014 	/* Next, set the netmask to all ones */
1015 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
1016 	sin6->sin6_family = AF_INET6;
1017 	(void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr));
1018 	if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
1019 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s",
1020 		    lifr.lifr_name);
1021 		goto failure;
1022 	}
1023 
1024 	/* Now set the interface address */
1025 	sin6->sin6_addr = *addr;
1026 	if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
1027 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s",
1028 		    lifr.lifr_name, abuf);
1029 		goto failure;
1030 	}
1031 
1032 	/* Mark the interface up */
1033 	if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
1034 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s",
1035 		    lifr.lifr_name);
1036 		goto failure;
1037 	}
1038 	lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING;
1039 	if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
1040 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s",
1041 		    lifr.lifr_name);
1042 		goto failure;
1043 	}
1044 
1045 	/* Now we can create the internal LIF structure */
1046 	hold_pif(pif);
1047 	if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL)
1048 		goto failure;
1049 
1050 	dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf,
1051 	    lif->lif_name);
1052 	lif->lif_plumbed = B_TRUE;
1053 
1054 	return (lif);
1055 
1056 failure:
1057 	if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
1058 	    errno != ENXIO) {
1059 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s",
1060 		    lifr.lifr_name);
1061 	}
1062 	return (NULL);
1063 }
1064 
1065 /*
1066  * unplumb_lif(): Removes the LIF from dhcpagent and the system.  This is used
1067  *		  for all interfaces configured by DHCP (those in leases).
1068  *
1069  *   input: dhcp_lif_t *: the interface to unplumb
1070  *  output: none
1071  */
1072 
1073 void
1074 unplumb_lif(dhcp_lif_t *lif)
1075 {
1076 	dhcp_lease_t *dlp;
1077 
1078 	if (lif->lif_plumbed) {
1079 		struct lifreq lifr;
1080 
1081 		(void) memset(&lifr, 0, sizeof (lifr));
1082 		(void) strlcpy(lifr.lifr_name, lif->lif_name,
1083 		    sizeof (lifr.lifr_name));
1084 		if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
1085 		    errno != ENXIO) {
1086 			dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s",
1087 			    lif->lif_name);
1088 		}
1089 		lif->lif_plumbed = B_FALSE;
1090 	}
1091 	lif->lif_flags = 0;
1092 	/*
1093 	 * Special case: if we're "unplumbing" the main LIF for DHCPv4, then
1094 	 * just canonize it and remove it from the lease.
1095 	 */
1096 	if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) {
1097 		canonize_lif(lif);
1098 		cancel_lif_timers(lif);
1099 		if (lif->lif_declined != NULL) {
1100 			dlp->dl_smach->dsm_lif_down--;
1101 			lif->lif_declined = NULL;
1102 		}
1103 		dlp->dl_nlifs = 0;
1104 		dlp->dl_lifs = NULL;
1105 		lif->lif_lease = NULL;
1106 		release_lif(lif);
1107 	} else {
1108 		remove_lif(lif);
1109 	}
1110 }
1111 
1112 /*
1113  * attach_lif(): create a new logical interface, creating the physical
1114  *		 interface as necessary.
1115  *
1116  *   input: const char *: the logical interface name
1117  *	    boolean_t: B_TRUE for IPv6
1118  *	    int *: set to DHCP_IPC_E_* if creation fails
1119  *  output: dhcp_lif_t *: pointer to new entry, or NULL on failure
1120  */
1121 
1122 dhcp_lif_t *
1123 attach_lif(const char *lname, boolean_t isv6, int *error)
1124 {
1125 	dhcp_pif_t *pif;
1126 	char pname[LIFNAMSIZ], *cp;
1127 
1128 	(void) strlcpy(pname, lname, sizeof (pname));
1129 	if ((cp = strchr(pname, ':')) != NULL)
1130 		*cp = '\0';
1131 
1132 	if ((pif = lookup_pif_by_name(pname, isv6)) != NULL)
1133 		hold_pif(pif);
1134 	else if ((pif = insert_pif(pname, isv6, error)) == NULL)
1135 		return (NULL);
1136 
1137 	if (lookup_lif_by_name(lname, pif) != NULL) {
1138 		dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!",
1139 		    lname);
1140 		release_pif(pif);
1141 		*error = DHCP_IPC_E_INVIF;
1142 		return (NULL);
1143 	}
1144 
1145 	/* If LIF creation fails, then insert_lif discards our PIF hold */
1146 	return (insert_lif(pif, lname, error));
1147 }
1148 
1149 /*
1150  * set_lif_dhcp(): Set logical interface flags to show that it's managed
1151  *		   by DHCP.
1152  *
1153  *   input: dhcp_lif_t *: the logical interface
1154  *	    boolean_t: B_TRUE if adopting
1155  *  output: int: set to DHCP_IPC_E_* if operation fails
1156  */
1157 
1158 int
1159 set_lif_dhcp(dhcp_lif_t *lif, boolean_t is_adopting)
1160 {
1161 	int fd;
1162 	int err;
1163 	struct lifreq lifr;
1164 
1165 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1166 
1167 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1168 
1169 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1170 		err = errno;
1171 		dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s",
1172 		    lif->lif_name);
1173 		return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT);
1174 	}
1175 	lif->lif_flags = lifr.lifr_flags;
1176 
1177 	/*
1178 	 * Check for conflicting sources of address control, and other
1179 	 * unacceptable configurations.
1180 	 */
1181 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_MIPRUNNING|
1182 	    IFF_TEMPORARY|IFF_VIRTUAL)) {
1183 		dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx",
1184 		    lif->lif_name, lifr.lifr_flags);
1185 		return (DHCP_IPC_E_INVIF);
1186 	}
1187 
1188 	/*
1189 	 * if DHCPRUNNING is already set on the interface and we're
1190 	 * not adopting it, the agent probably crashed and burned.
1191 	 * note it, but don't let it stop the proceedings.  we're
1192 	 * pretty sure we're not already running, since we wouldn't
1193 	 * have been able to bind to our IPC port.
1194 	 */
1195 
1196 	if (lifr.lifr_flags & IFF_DHCPRUNNING) {
1197 		if (!is_adopting) {
1198 			dhcpmsg(MSG_WARNING, "set_lif_dhcp: DHCP flag already "
1199 			    "set on %s", lif->lif_name);
1200 		}
1201 	} else {
1202 		lifr.lifr_flags |= IFF_DHCPRUNNING;
1203 		if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
1204 			dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s",
1205 			    lif->lif_name);
1206 			return (DHCP_IPC_E_INT);
1207 		}
1208 		lif->lif_flags = lifr.lifr_flags;
1209 	}
1210 	return (DHCP_IPC_SUCCESS);
1211 }
1212 
1213 /*
1214  * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer
1215  *		     managed by DHCP.
1216  *
1217  *   input: dhcp_lif_t *: the logical interface
1218  *  output: none
1219  */
1220 
1221 static void
1222 clear_lif_dhcp(dhcp_lif_t *lif)
1223 {
1224 	int fd;
1225 	struct lifreq lifr;
1226 
1227 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1228 
1229 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1230 
1231 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
1232 		return;
1233 
1234 	if (!(lifr.lifr_flags & IFF_DHCPRUNNING))
1235 		return;
1236 
1237 	lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING;
1238 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
1239 }
1240 
1241 /*
1242  * set_lif_deprecated(): Set the "deprecated" flag to tell users that this
1243  *			 address will be going away.  As the interface is
1244  *			 going away, we don't care if there are errors.
1245  *
1246  *   input: dhcp_lif_t *: the logical interface
1247  *  output: none
1248  */
1249 
1250 void
1251 set_lif_deprecated(dhcp_lif_t *lif)
1252 {
1253 	int fd;
1254 	struct lifreq lifr;
1255 
1256 	if (lif->lif_flags & IFF_DEPRECATED)
1257 		return;
1258 
1259 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1260 
1261 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1262 
1263 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
1264 		return;
1265 
1266 	if (lifr.lifr_flags & IFF_DEPRECATED)
1267 		return;
1268 
1269 	lifr.lifr_flags |= IFF_DEPRECATED;
1270 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
1271 	lif->lif_flags = lifr.lifr_flags;
1272 }
1273 
1274 /*
1275  * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this
1276  *			   address will not be going away.  This happens if we
1277  *			   get a renewal after preferred lifetime but before
1278  *			   the valid lifetime.
1279  *
1280  *   input: dhcp_lif_t *: the logical interface
1281  *  output: boolean_t: B_TRUE on success.
1282  */
1283 
1284 boolean_t
1285 clear_lif_deprecated(dhcp_lif_t *lif)
1286 {
1287 	int fd;
1288 	struct lifreq lifr;
1289 
1290 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1291 
1292 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1293 
1294 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1295 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s",
1296 		    lif->lif_name);
1297 		return (B_FALSE);
1298 	}
1299 
1300 	/*
1301 	 * Check for conflicting sources of address control, and other
1302 	 * unacceptable configurations.
1303 	 */
1304 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_MIPRUNNING|
1305 	    IFF_TEMPORARY|IFF_VIRTUAL)) {
1306 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags "
1307 		    "are %llx", lif->lif_name, lifr.lifr_flags);
1308 		return (B_FALSE);
1309 	}
1310 
1311 	if (!(lifr.lifr_flags & IFF_DEPRECATED))
1312 		return (B_TRUE);
1313 
1314 	lifr.lifr_flags &= ~IFF_DEPRECATED;
1315 	if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
1316 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s",
1317 		    lif->lif_name);
1318 		return (B_FALSE);
1319 	} else {
1320 		lif->lif_flags = lifr.lifr_flags;
1321 		return (B_TRUE);
1322 	}
1323 }
1324 
1325 /*
1326  * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only).
1327  *
1328  *   input: dhcp_lif_t *: the logical interface to operate on
1329  *  output: boolean_t: B_TRUE if the socket was opened successfully.
1330  */
1331 
1332 boolean_t
1333 open_ip_lif(dhcp_lif_t *lif)
1334 {
1335 	if (lif->lif_sock_ip_fd != -1) {
1336 		dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s",
1337 		    lif->lif_name);
1338 		return (B_FALSE);
1339 	}
1340 
1341 	lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
1342 	if (lif->lif_sock_ip_fd == -1) {
1343 		dhcpmsg(MSG_ERR, "open_ip_lif: cannot create v4 socket on %s",
1344 		    lif->lif_name);
1345 		return (B_FALSE);
1346 	}
1347 
1348 	if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC,
1349 	    ntohl(lif->lif_addr))) {
1350 		dhcpmsg(MSG_ERR, "open_ip_lif: cannot bind v4 socket on %s",
1351 		    lif->lif_name);
1352 		return (B_FALSE);
1353 	}
1354 
1355 	lif->lif_acknak_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN,
1356 	    dhcp_acknak_lif, lif);
1357 	if (lif->lif_acknak_id == -1) {
1358 		dhcpmsg(MSG_WARNING, "open_ip_lif: cannot register to "
1359 		    "receive IP unicast");
1360 		close_ip_lif(lif);
1361 		return (B_FALSE);
1362 	}
1363 	return (B_TRUE);
1364 }
1365 
1366 /*
1367  * close_ip_lif(): close an IP socket for I/O on a given LIF.
1368  *
1369  *   input: dhcp_lif_t *: the logical interface to operate on
1370  *  output: none
1371  */
1372 
1373 void
1374 close_ip_lif(dhcp_lif_t *lif)
1375 {
1376 	if (lif->lif_acknak_id != -1) {
1377 		(void) iu_unregister_event(eh, lif->lif_acknak_id, NULL);
1378 		lif->lif_acknak_id = -1;
1379 	}
1380 	if (lif->lif_sock_ip_fd != -1) {
1381 		(void) close(lif->lif_sock_ip_fd);
1382 		lif->lif_sock_ip_fd = -1;
1383 	}
1384 }
1385 
1386 /*
1387  * lif_mark_decline(): mark a LIF as having been declined due to a duplicate
1388  *		       address or some other conflict.  This is used in
1389  *		       send_declines() to report failure back to the server.
1390  *
1391  *   input: dhcp_lif_t *: the logical interface to operate on
1392  *	    const char *: text string explaining why the address is declined
1393  *  output: none
1394  */
1395 
1396 void
1397 lif_mark_decline(dhcp_lif_t *lif, const char *reason)
1398 {
1399 	if (lif->lif_declined == NULL) {
1400 		dhcp_lease_t *dlp;
1401 
1402 		lif->lif_declined = reason;
1403 		if ((dlp = lif->lif_lease) != NULL)
1404 			dlp->dl_smach->dsm_lif_down++;
1405 	}
1406 }
1407 
1408 /*
1409  * schedule_lif_timer(): schedules the LIF-related timer
1410  *
1411  *   input: dhcp_lif_t *: the logical interface to operate on
1412  *	    dhcp_timer_t *: the timer to schedule
1413  *	    iu_tq_callback_t *: the callback to call upon firing
1414  *  output: boolean_t: B_TRUE if the timer was scheduled successfully
1415  */
1416 
1417 boolean_t
1418 schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire)
1419 {
1420 	/*
1421 	 * If there's a timer running, cancel it and release its lease
1422 	 * reference.
1423 	 */
1424 	if (dt->dt_id != -1) {
1425 		if (!cancel_timer(dt))
1426 			return (B_FALSE);
1427 		release_lif(lif);
1428 	}
1429 
1430 	if (schedule_timer(dt, expire, lif)) {
1431 		hold_lif(lif);
1432 		return (B_TRUE);
1433 	} else {
1434 		dhcpmsg(MSG_WARNING,
1435 		    "schedule_lif_timer: cannot schedule timer");
1436 		return (B_FALSE);
1437 	}
1438 }
1439 
1440 /*
1441  * cancel_lif_timer(): cancels a LIF-related timer
1442  *
1443  *   input: dhcp_lif_t *: the logical interface to operate on
1444  *	    dhcp_timer_t *: the timer to cancel
1445  *  output: none
1446  */
1447 
1448 static void
1449 cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt)
1450 {
1451 	if (dt->dt_id == -1)
1452 		return;
1453 	if (cancel_timer(dt)) {
1454 		dhcpmsg(MSG_DEBUG2,
1455 		    "cancel_lif_timer: canceled expiry timer on %s",
1456 		    lif->lif_name);
1457 		release_lif(lif);
1458 	} else {
1459 		dhcpmsg(MSG_WARNING,
1460 		    "cancel_lif_timer: cannot cancel timer on %s",
1461 		    lif->lif_name);
1462 	}
1463 }
1464 
1465 /*
1466  * cancel_lif_timers(): cancels the LIF-related timers
1467  *
1468  *   input: dhcp_lif_t *: the logical interface to operate on
1469  *  output: none
1470  */
1471 
1472 void
1473 cancel_lif_timers(dhcp_lif_t *lif)
1474 {
1475 	cancel_lif_timer(lif, &lif->lif_preferred);
1476 	cancel_lif_timer(lif, &lif->lif_expire);
1477 }
1478 
1479 /*
1480  * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common
1481  *		  file descriptors (v4_sock_fd and v6_sock_fd).
1482  *
1483  *   input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4
1484  *  output: none
1485  */
1486 
1487 uint_t
1488 get_max_mtu(boolean_t isv6)
1489 {
1490 	uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu;
1491 
1492 	if (*mtup == 0) {
1493 		dhcp_pif_t *pif;
1494 		dhcp_lif_t *lif;
1495 		struct lifreq lifr;
1496 
1497 		/* Set an arbitrary lower bound */
1498 		*mtup = 1024;
1499 		pif = isv6 ? v6root : v4root;
1500 		for (; pif != NULL; pif = pif->pif_next) {
1501 			for (lif = pif->pif_lifs; lif != NULL;
1502 			    lif = lif->lif_next) {
1503 				(void) strlcpy(lifr.lifr_name, lif->lif_name,
1504 				    LIFNAMSIZ);
1505 				if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) !=
1506 				    -1 && lifr.lifr_mtu > *mtup) {
1507 					*mtup = lifr.lifr_mtu;
1508 				}
1509 			}
1510 		}
1511 	}
1512 	return (*mtup);
1513 }
1514 
1515 /*
1516  * expired_lif_state(): summarize the state of expired LIFs on a given state
1517  *			machine.
1518  *
1519  *   input: dhcp_smach_t *: the state machine to scan
1520  *  output: dhcp_expire_t: overall state
1521  */
1522 
1523 dhcp_expire_t
1524 expired_lif_state(dhcp_smach_t *dsmp)
1525 {
1526 	dhcp_lease_t *dlp;
1527 	dhcp_lif_t *lif;
1528 	uint_t nlifs;
1529 	uint_t numlifs;
1530 	uint_t numexp;
1531 
1532 	numlifs = numexp = 0;
1533 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
1534 		lif = dlp->dl_lifs;
1535 		nlifs = dlp->dl_nlifs;
1536 		numlifs += nlifs;
1537 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
1538 			if (lif->lif_expired)
1539 				numexp++;
1540 		}
1541 	}
1542 	if (numlifs == 0)
1543 		return (DHCP_EXP_NOLIFS);
1544 	else if (numexp == 0)
1545 		return (DHCP_EXP_NOEXP);
1546 	else if (numlifs == numexp)
1547 		return (DHCP_EXP_ALLEXP);
1548 	else
1549 		return (DHCP_EXP_SOMEEXP);
1550 }
1551 
1552 /*
1553  * find_expired_lif(): find the first expired LIF on a given state machine
1554  *
1555  *   input: dhcp_smach_t *: the state machine to scan
1556  *  output: dhcp_lif_t *: the first expired LIF, or NULL if none.
1557  */
1558 
1559 dhcp_lif_t *
1560 find_expired_lif(dhcp_smach_t *dsmp)
1561 {
1562 	dhcp_lease_t *dlp;
1563 	dhcp_lif_t *lif;
1564 	uint_t nlifs;
1565 
1566 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
1567 		lif = dlp->dl_lifs;
1568 		nlifs = dlp->dl_nlifs;
1569 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
1570 			if (lif->lif_expired)
1571 				return (lif);
1572 		}
1573 	}
1574 	return (NULL);
1575 }
1576 
1577 /*
1578  * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING.  Used
1579  *		       only for DHCPv6.
1580  *
1581  *   input: none
1582  *  output: none
1583  */
1584 
1585 void
1586 remove_v6_strays(void)
1587 {
1588 	struct lifnum lifn;
1589 	struct lifconf lifc;
1590 	struct lifreq *lifrp, *lifrmax;
1591 	uint_t numifs;
1592 	uint64_t flags;
1593 
1594 	/*
1595 	 * Get the approximate number of interfaces in the system.  It's only
1596 	 * approximate because the system is dynamic -- interfaces may be
1597 	 * plumbed or unplumbed at any time.  This is also the reason for the
1598 	 * "+ 10" fudge factor: we're trying to avoid unnecessary looping.
1599 	 */
1600 	(void) memset(&lifn, 0, sizeof (lifn));
1601 	lifn.lifn_family = AF_INET6;
1602 	lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
1603 	if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) {
1604 		dhcpmsg(MSG_ERR,
1605 		    "remove_v6_strays: cannot read number of interfaces");
1606 		numifs = 10;
1607 	} else {
1608 		numifs = lifn.lifn_count + 10;
1609 	}
1610 
1611 	/*
1612 	 * Get the interface information.  We do this in a loop so that we can
1613 	 * recover from EINVAL from the kernel -- delivered when the buffer is
1614 	 * too small.
1615 	 */
1616 	(void) memset(&lifc, 0, sizeof (lifc));
1617 	lifc.lifc_family = AF_INET6;
1618 	lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
1619 	for (;;) {
1620 		lifc.lifc_len = numifs * sizeof (*lifrp);
1621 		lifrp = realloc(lifc.lifc_buf, lifc.lifc_len);
1622 		if (lifrp == NULL) {
1623 			dhcpmsg(MSG_ERR,
1624 			    "remove_v6_strays: cannot allocate memory");
1625 			free(lifc.lifc_buf);
1626 			return;
1627 		}
1628 		lifc.lifc_buf = (caddr_t)lifrp;
1629 		errno = 0;
1630 		if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 &&
1631 		    lifc.lifc_len < numifs * sizeof (*lifrp))
1632 			break;
1633 		if (errno == 0 || errno == EINVAL) {
1634 			numifs <<= 1;
1635 		} else {
1636 			dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF");
1637 			free(lifc.lifc_buf);
1638 			return;
1639 		}
1640 	}
1641 
1642 	lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp);
1643 	for (; lifrp < lifrmax; lifrp++) {
1644 		/*
1645 		 * Get the interface flags; we're interested in the DHCP ones.
1646 		 */
1647 		if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1)
1648 			continue;
1649 		flags = lifrp->lifr_flags;
1650 		if (!(flags & IFF_DHCPRUNNING))
1651 			continue;
1652 		/*
1653 		 * If the interface has a link-local address, then we don't
1654 		 * control it.  Just remove the flag.
1655 		 */
1656 		if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1)
1657 			continue;
1658 		if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp->
1659 		    lifr_addr)->sin6_addr)) {
1660 			lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING;
1661 			(void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp);
1662 			continue;
1663 		}
1664 		/*
1665 		 * All others are (or were) under our control.  Clean up by
1666 		 * removing them.
1667 		 */
1668 		if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) {
1669 			dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s",
1670 			    lifrp->lifr_name);
1671 		} else if (errno != ENXIO) {
1672 			dhcpmsg(MSG_ERR,
1673 			    "remove_v6_strays: SIOCLIFREMOVEIF %s",
1674 			    lifrp->lifr_name);
1675 		}
1676 	}
1677 	free(lifc.lifc_buf);
1678 }
1679