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