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