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