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