xref: /illumos-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c (revision 9525b14bcdeb5b5f6f95ab27c2f48f18bd2ec829)
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  *  output: int: set to DHCP_IPC_E_* if operation fails
1120  */
1121 
1122 int
1123 set_lif_dhcp(dhcp_lif_t *lif)
1124 {
1125 	int fd;
1126 	int err;
1127 	struct lifreq lifr;
1128 	dhcp_pif_t *pif = lif->lif_pif;
1129 
1130 	fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1131 
1132 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1133 
1134 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1135 		err = errno;
1136 		dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s",
1137 		    lif->lif_name);
1138 		return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT);
1139 	}
1140 	lif->lif_flags = lifr.lifr_flags;
1141 
1142 	/*
1143 	 * Check for conflicting sources of address control, and other
1144 	 * unacceptable configurations.
1145 	 */
1146 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
1147 	    IFF_VIRTUAL)) {
1148 		dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx",
1149 		    lif->lif_name, lifr.lifr_flags);
1150 		return (DHCP_IPC_E_INVIF);
1151 	}
1152 
1153 	/*
1154 	 * If IFF_DHCPRUNNING is already set on the interface and we're not
1155 	 * adopting it, the agent probably crashed and burned.  Note it, but
1156 	 * don't let it stop the proceedings (we're pretty sure we're not
1157 	 * already running, since we were able to bind to our IPC port).
1158 	 */
1159 	if (lifr.lifr_flags & IFF_DHCPRUNNING) {
1160 		dhcpmsg(MSG_VERBOSE, "set_lif_dhcp: IFF_DHCPRUNNING already set"
1161 		    " on %s", lif->lif_name);
1162 	} else {
1163 		/*
1164 		 * If the lif is on an interface under IPMP, IFF_NOFAILOVER
1165 		 * must be set or the kernel will prevent us from setting
1166 		 * IFF_DHCPRUNNING (since the subsequent IFF_UP would lead to
1167 		 * migration).  We set IFF_DEPRECATED too since the kernel
1168 		 * will set it automatically when setting IFF_NOFAILOVER,
1169 		 * causing our lif_flags value to grow stale.
1170 		 */
1171 		if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER))
1172 			lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
1173 
1174 		lifr.lifr_flags |= IFF_DHCPRUNNING;
1175 		if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
1176 			dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s",
1177 			    lif->lif_name);
1178 			return (DHCP_IPC_E_INT);
1179 		}
1180 		lif->lif_flags = lifr.lifr_flags;
1181 	}
1182 	return (DHCP_IPC_SUCCESS);
1183 }
1184 
1185 /*
1186  * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer
1187  *		     managed by DHCP.
1188  *
1189  *   input: dhcp_lif_t *: the logical interface
1190  *  output: none
1191  */
1192 
1193 static void
1194 clear_lif_dhcp(dhcp_lif_t *lif)
1195 {
1196 	int fd;
1197 	struct lifreq lifr;
1198 
1199 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1200 
1201 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1202 
1203 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
1204 		return;
1205 
1206 	if (!(lifr.lifr_flags & IFF_DHCPRUNNING))
1207 		return;
1208 
1209 	lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING;
1210 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
1211 }
1212 
1213 /*
1214  * set_lif_deprecated(): Set the "deprecated" flag to tell users that this
1215  *			 address will be going away.  As the interface is
1216  *			 going away, we don't care if there are errors.
1217  *
1218  *   input: dhcp_lif_t *: the logical interface
1219  *  output: none
1220  */
1221 
1222 void
1223 set_lif_deprecated(dhcp_lif_t *lif)
1224 {
1225 	int fd;
1226 	struct lifreq lifr;
1227 
1228 	if (lif->lif_flags & IFF_DEPRECATED)
1229 		return;
1230 
1231 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1232 
1233 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1234 
1235 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
1236 		return;
1237 
1238 	if (lifr.lifr_flags & IFF_DEPRECATED)
1239 		return;
1240 
1241 	lifr.lifr_flags |= IFF_DEPRECATED;
1242 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
1243 	lif->lif_flags = lifr.lifr_flags;
1244 }
1245 
1246 /*
1247  * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this
1248  *			   address will not be going away.  This happens if we
1249  *			   get a renewal after preferred lifetime but before
1250  *			   the valid lifetime.
1251  *
1252  *   input: dhcp_lif_t *: the logical interface
1253  *  output: boolean_t: B_TRUE on success.
1254  */
1255 
1256 boolean_t
1257 clear_lif_deprecated(dhcp_lif_t *lif)
1258 {
1259 	int fd;
1260 	struct lifreq lifr;
1261 
1262 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1263 
1264 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1265 
1266 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1267 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s",
1268 		    lif->lif_name);
1269 		return (B_FALSE);
1270 	}
1271 
1272 	/*
1273 	 * Check for conflicting sources of address control, and other
1274 	 * unacceptable configurations.
1275 	 */
1276 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
1277 	    IFF_VIRTUAL)) {
1278 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags "
1279 		    "are %llx", lif->lif_name, lifr.lifr_flags);
1280 		return (B_FALSE);
1281 	}
1282 
1283 	/*
1284 	 * Don't try to clear IFF_DEPRECATED if this is a test address,
1285 	 * since IPMP's use of IFF_DEPRECATED is not compatible with ours.
1286 	 */
1287 	if (lifr.lifr_flags & IFF_NOFAILOVER)
1288 		return (B_TRUE);
1289 
1290 	if (!(lifr.lifr_flags & IFF_DEPRECATED))
1291 		return (B_TRUE);
1292 
1293 	lifr.lifr_flags &= ~IFF_DEPRECATED;
1294 	if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
1295 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s",
1296 		    lif->lif_name);
1297 		return (B_FALSE);
1298 	} else {
1299 		lif->lif_flags = lifr.lifr_flags;
1300 		return (B_TRUE);
1301 	}
1302 }
1303 
1304 /*
1305  * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only).
1306  *
1307  *   input: dhcp_lif_t *: the logical interface to operate on
1308  *	    in_addr_t: the address the socket will be bound to (in hbo)
1309  *	    boolean_t: B_TRUE if the address should be brought up (if needed)
1310  *  output: boolean_t: B_TRUE if the socket was opened successfully.
1311  */
1312 
1313 boolean_t
1314 open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo, boolean_t bringup)
1315 {
1316 	const char *errmsg;
1317 	struct lifreq lifr;
1318 	int on = 1;
1319 	uchar_t ttl = 255;
1320 	uint32_t ifindex;
1321 	dhcp_pif_t *pif = lif->lif_pif;
1322 
1323 	if (lif->lif_sock_ip_fd != -1) {
1324 		dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s",
1325 		    lif->lif_name);
1326 		return (B_FALSE);
1327 	}
1328 
1329 	lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
1330 	if (lif->lif_sock_ip_fd == -1) {
1331 		errmsg = "cannot create v4 socket";
1332 		goto failure;
1333 	}
1334 
1335 	if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, addr_hbo)) {
1336 		errmsg = "cannot bind v4 socket";
1337 		goto failure;
1338 	}
1339 
1340 	/*
1341 	 * If we bound to INADDR_ANY, we have no IFF_UP source address to use.
1342 	 * Thus, enable IP_UNSPEC_SRC so that we can send packets with an
1343 	 * unspecified (0.0.0.0) address.  Also, enable IP_DHCPINIT_IF so that
1344 	 * the IP module will accept unicast DHCP traffic regardless of the IP
1345 	 * address it's sent to.  (We'll then figure out which packets are
1346 	 * ours based on the xid.)
1347 	 */
1348 	if (addr_hbo == INADDR_ANY) {
1349 		if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_UNSPEC_SRC,
1350 		    &on, sizeof (int)) == -1) {
1351 			errmsg = "cannot set IP_UNSPEC_SRC";
1352 			goto failure;
1353 		}
1354 
1355 		if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_DHCPINIT_IF,
1356 		    &pif->pif_index, sizeof (int)) == -1) {
1357 			errmsg = "cannot set IP_DHCPINIT_IF";
1358 			goto failure;
1359 		}
1360 	}
1361 
1362 	/*
1363 	 * Unfortunately, some hardware (such as the Linksys WRT54GC)
1364 	 * decrements the TTL *prior* to accepting DHCP traffic destined
1365 	 * for it.  To workaround this, tell IP to use a TTL of 255 for
1366 	 * broadcast packets sent from this socket.
1367 	 */
1368 	if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BROADCAST_TTL, &ttl,
1369 	    sizeof (uchar_t)) == -1) {
1370 		errmsg = "cannot set IP_BROADCAST_TTL";
1371 		goto failure;
1372 	}
1373 
1374 	ifindex = pif->pif_under_ipmp ? pif->pif_grindex : pif->pif_index;
1375 	if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF, &ifindex,
1376 	    sizeof (int)) == -1) {
1377 		errmsg = "cannot set IP_BOUND_IF";
1378 		goto failure;
1379 	}
1380 
1381 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1382 	if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
1383 		errmsg = "cannot get interface flags";
1384 		goto failure;
1385 	}
1386 
1387 	/*
1388 	 * If the lif is part of an interface under IPMP, IFF_NOFAILOVER must
1389 	 * be set or the kernel will prevent us from setting IFF_DHCPRUNNING
1390 	 * (since the subsequent IFF_UP would lead to migration).  We set
1391 	 * IFF_DEPRECATED too since the kernel will set it automatically when
1392 	 * setting IFF_NOFAILOVER, causing our lif_flags value to grow stale.
1393 	 */
1394 	if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER)) {
1395 		lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
1396 		if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
1397 			errmsg = "cannot set IFF_NOFAILOVER";
1398 			goto failure;
1399 		}
1400 	}
1401 	lif->lif_flags = lifr.lifr_flags;
1402 
1403 	/*
1404 	 * If this is initial bringup, make sure the address we're acquiring a
1405 	 * lease on is IFF_UP.
1406 	 */
1407 	if (bringup && !(lifr.lifr_flags & IFF_UP)) {
1408 		/*
1409 		 * Start from a clean slate.
1410 		 */
1411 		canonize_lif(lif, B_FALSE);
1412 
1413 		lifr.lifr_flags |= IFF_UP;
1414 		if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
1415 			errmsg = "cannot bring up";
1416 			goto failure;
1417 		}
1418 		lif->lif_flags = lifr.lifr_flags;
1419 
1420 		/*
1421 		 * When bringing 0.0.0.0 IFF_UP, the kernel changes the
1422 		 * netmask to 255.0.0.0, so re-fetch our expected netmask.
1423 		 */
1424 		if (ioctl(v4_sock_fd, SIOCGLIFNETMASK, &lifr) == -1) {
1425 			errmsg = "cannot get netmask";
1426 			goto failure;
1427 		}
1428 
1429 		lif->lif_netmask =
1430 		    ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr;
1431 	}
1432 
1433 	/*
1434 	 * Usually, bringing up the address we're acquiring a lease on is
1435 	 * sufficient to allow packets to be sent and received via the
1436 	 * IP_BOUND_IF we did earlier.  However, if we're acquiring a lease on
1437 	 * an underlying IPMP interface, the group interface will be used for
1438 	 * sending and receiving IP packets via IP_BOUND_IF.  Thus, ensure at
1439 	 * least one address on the group interface is IFF_UP.
1440 	 */
1441 	if (bringup && pif->pif_under_ipmp) {
1442 		(void) strlcpy(lifr.lifr_name, pif->pif_grifname, LIFNAMSIZ);
1443 		if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
1444 			errmsg = "cannot get IPMP group interface flags";
1445 			goto failure;
1446 		}
1447 
1448 		if (!(lifr.lifr_flags & IFF_UP)) {
1449 			lifr.lifr_flags |= IFF_UP;
1450 			if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
1451 				errmsg = "cannot bring up IPMP group interface";
1452 				goto failure;
1453 			}
1454 		}
1455 	}
1456 
1457 	lif->lif_packet_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN,
1458 	    dhcp_packet_lif, lif);
1459 	if (lif->lif_packet_id == -1) {
1460 		errmsg = "cannot register to receive DHCP packets";
1461 		goto failure;
1462 	}
1463 
1464 	return (B_TRUE);
1465 failure:
1466 	dhcpmsg(MSG_ERR, "open_ip_lif: %s: %s", lif->lif_name, errmsg);
1467 	close_ip_lif(lif);
1468 	return (B_FALSE);
1469 }
1470 
1471 /*
1472  * close_ip_lif(): close an IP socket for I/O on a given LIF.
1473  *
1474  *   input: dhcp_lif_t *: the logical interface to operate on
1475  *  output: none
1476  */
1477 
1478 void
1479 close_ip_lif(dhcp_lif_t *lif)
1480 {
1481 	if (lif->lif_packet_id != -1) {
1482 		(void) iu_unregister_event(eh, lif->lif_packet_id, NULL);
1483 		lif->lif_packet_id = -1;
1484 	}
1485 	if (lif->lif_sock_ip_fd != -1) {
1486 		(void) close(lif->lif_sock_ip_fd);
1487 		lif->lif_sock_ip_fd = -1;
1488 	}
1489 }
1490 
1491 /*
1492  * lif_mark_decline(): mark a LIF as having been declined due to a duplicate
1493  *		       address or some other conflict.  This is used in
1494  *		       send_declines() to report failure back to the server.
1495  *
1496  *   input: dhcp_lif_t *: the logical interface to operate on
1497  *	    const char *: text string explaining why the address is declined
1498  *  output: none
1499  */
1500 
1501 void
1502 lif_mark_decline(dhcp_lif_t *lif, const char *reason)
1503 {
1504 	if (lif->lif_declined == NULL) {
1505 		dhcp_lease_t *dlp;
1506 
1507 		lif->lif_declined = reason;
1508 		if ((dlp = lif->lif_lease) != NULL)
1509 			dlp->dl_smach->dsm_lif_down++;
1510 	}
1511 }
1512 
1513 /*
1514  * schedule_lif_timer(): schedules the LIF-related timer
1515  *
1516  *   input: dhcp_lif_t *: the logical interface to operate on
1517  *	    dhcp_timer_t *: the timer to schedule
1518  *	    iu_tq_callback_t *: the callback to call upon firing
1519  *  output: boolean_t: B_TRUE if the timer was scheduled successfully
1520  */
1521 
1522 boolean_t
1523 schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire)
1524 {
1525 	/*
1526 	 * If there's a timer running, cancel it and release its lease
1527 	 * reference.
1528 	 */
1529 	if (dt->dt_id != -1) {
1530 		if (!cancel_timer(dt))
1531 			return (B_FALSE);
1532 		release_lif(lif);
1533 	}
1534 
1535 	if (schedule_timer(dt, expire, lif)) {
1536 		hold_lif(lif);
1537 		return (B_TRUE);
1538 	} else {
1539 		dhcpmsg(MSG_WARNING,
1540 		    "schedule_lif_timer: cannot schedule timer");
1541 		return (B_FALSE);
1542 	}
1543 }
1544 
1545 /*
1546  * cancel_lif_timer(): cancels a LIF-related timer
1547  *
1548  *   input: dhcp_lif_t *: the logical interface to operate on
1549  *	    dhcp_timer_t *: the timer to cancel
1550  *  output: none
1551  */
1552 
1553 static void
1554 cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt)
1555 {
1556 	if (dt->dt_id == -1)
1557 		return;
1558 	if (cancel_timer(dt)) {
1559 		dhcpmsg(MSG_DEBUG2,
1560 		    "cancel_lif_timer: canceled expiry timer on %s",
1561 		    lif->lif_name);
1562 		release_lif(lif);
1563 	} else {
1564 		dhcpmsg(MSG_WARNING,
1565 		    "cancel_lif_timer: cannot cancel timer on %s",
1566 		    lif->lif_name);
1567 	}
1568 }
1569 
1570 /*
1571  * cancel_lif_timers(): cancels the LIF-related timers
1572  *
1573  *   input: dhcp_lif_t *: the logical interface to operate on
1574  *  output: none
1575  */
1576 
1577 void
1578 cancel_lif_timers(dhcp_lif_t *lif)
1579 {
1580 	cancel_lif_timer(lif, &lif->lif_preferred);
1581 	cancel_lif_timer(lif, &lif->lif_expire);
1582 }
1583 
1584 /*
1585  * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common
1586  *		  file descriptors (v4_sock_fd and v6_sock_fd).
1587  *
1588  *   input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4
1589  *  output: none
1590  */
1591 
1592 uint_t
1593 get_max_mtu(boolean_t isv6)
1594 {
1595 	uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu;
1596 
1597 	if (*mtup == 0) {
1598 		dhcp_pif_t *pif;
1599 		dhcp_lif_t *lif;
1600 		struct lifreq lifr;
1601 
1602 		/* Set an arbitrary lower bound */
1603 		*mtup = 1024;
1604 		pif = isv6 ? v6root : v4root;
1605 		for (; pif != NULL; pif = pif->pif_next) {
1606 			for (lif = pif->pif_lifs; lif != NULL;
1607 			    lif = lif->lif_next) {
1608 				(void) strlcpy(lifr.lifr_name, lif->lif_name,
1609 				    LIFNAMSIZ);
1610 				if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) !=
1611 				    -1 && lifr.lifr_mtu > *mtup) {
1612 					*mtup = lifr.lifr_mtu;
1613 				}
1614 			}
1615 		}
1616 	}
1617 	return (*mtup);
1618 }
1619 
1620 /*
1621  * expired_lif_state(): summarize the state of expired LIFs on a given state
1622  *			machine.
1623  *
1624  *   input: dhcp_smach_t *: the state machine to scan
1625  *  output: dhcp_expire_t: overall state
1626  */
1627 
1628 dhcp_expire_t
1629 expired_lif_state(dhcp_smach_t *dsmp)
1630 {
1631 	dhcp_lease_t *dlp;
1632 	dhcp_lif_t *lif;
1633 	uint_t nlifs;
1634 	uint_t numlifs;
1635 	uint_t numexp;
1636 
1637 	numlifs = numexp = 0;
1638 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
1639 		lif = dlp->dl_lifs;
1640 		nlifs = dlp->dl_nlifs;
1641 		numlifs += nlifs;
1642 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
1643 			if (lif->lif_expired)
1644 				numexp++;
1645 		}
1646 	}
1647 	if (numlifs == 0)
1648 		return (DHCP_EXP_NOLIFS);
1649 	else if (numexp == 0)
1650 		return (DHCP_EXP_NOEXP);
1651 	else if (numlifs == numexp)
1652 		return (DHCP_EXP_ALLEXP);
1653 	else
1654 		return (DHCP_EXP_SOMEEXP);
1655 }
1656 
1657 /*
1658  * find_expired_lif(): find the first expired LIF on a given state machine
1659  *
1660  *   input: dhcp_smach_t *: the state machine to scan
1661  *  output: dhcp_lif_t *: the first expired LIF, or NULL if none.
1662  */
1663 
1664 dhcp_lif_t *
1665 find_expired_lif(dhcp_smach_t *dsmp)
1666 {
1667 	dhcp_lease_t *dlp;
1668 	dhcp_lif_t *lif;
1669 	uint_t nlifs;
1670 
1671 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
1672 		lif = dlp->dl_lifs;
1673 		nlifs = dlp->dl_nlifs;
1674 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
1675 			if (lif->lif_expired)
1676 				return (lif);
1677 		}
1678 	}
1679 	return (NULL);
1680 }
1681 
1682 /*
1683  * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING.  Used
1684  *		       only for DHCPv6.
1685  *
1686  *   input: none
1687  *  output: none
1688  */
1689 
1690 void
1691 remove_v6_strays(void)
1692 {
1693 	struct lifnum lifn;
1694 	struct lifconf lifc;
1695 	struct lifreq *lifrp, *lifrmax;
1696 	uint_t numifs;
1697 	uint64_t flags;
1698 
1699 	/*
1700 	 * Get the approximate number of interfaces in the system.  It's only
1701 	 * approximate because the system is dynamic -- interfaces may be
1702 	 * plumbed or unplumbed at any time.  This is also the reason for the
1703 	 * "+ 10" fudge factor: we're trying to avoid unnecessary looping.
1704 	 */
1705 	(void) memset(&lifn, 0, sizeof (lifn));
1706 	lifn.lifn_family = AF_INET6;
1707 	lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
1708 	if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) {
1709 		dhcpmsg(MSG_ERR,
1710 		    "remove_v6_strays: cannot read number of interfaces");
1711 		numifs = 10;
1712 	} else {
1713 		numifs = lifn.lifn_count + 10;
1714 	}
1715 
1716 	/*
1717 	 * Get the interface information.  We do this in a loop so that we can
1718 	 * recover from EINVAL from the kernel -- delivered when the buffer is
1719 	 * too small.
1720 	 */
1721 	(void) memset(&lifc, 0, sizeof (lifc));
1722 	lifc.lifc_family = AF_INET6;
1723 	lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
1724 	for (;;) {
1725 		lifc.lifc_len = numifs * sizeof (*lifrp);
1726 		lifrp = realloc(lifc.lifc_buf, lifc.lifc_len);
1727 		if (lifrp == NULL) {
1728 			dhcpmsg(MSG_ERR,
1729 			    "remove_v6_strays: cannot allocate memory");
1730 			free(lifc.lifc_buf);
1731 			return;
1732 		}
1733 		lifc.lifc_buf = (caddr_t)lifrp;
1734 		errno = 0;
1735 		if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 &&
1736 		    lifc.lifc_len < numifs * sizeof (*lifrp))
1737 			break;
1738 		if (errno == 0 || errno == EINVAL) {
1739 			numifs <<= 1;
1740 		} else {
1741 			dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF");
1742 			free(lifc.lifc_buf);
1743 			return;
1744 		}
1745 	}
1746 
1747 	lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp);
1748 	for (; lifrp < lifrmax; lifrp++) {
1749 		/*
1750 		 * Get the interface flags; we're interested in the DHCP ones.
1751 		 */
1752 		if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1)
1753 			continue;
1754 		flags = lifrp->lifr_flags;
1755 		if (!(flags & IFF_DHCPRUNNING))
1756 			continue;
1757 		/*
1758 		 * If the interface has a link-local address, then we don't
1759 		 * control it.  Just remove the flag.
1760 		 */
1761 		if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1)
1762 			continue;
1763 		if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp->
1764 		    lifr_addr)->sin6_addr)) {
1765 			lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING;
1766 			(void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp);
1767 			continue;
1768 		}
1769 		/*
1770 		 * All others are (or were) under our control.  Clean up by
1771 		 * removing them.
1772 		 */
1773 		if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) {
1774 			dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s",
1775 			    lifrp->lifr_name);
1776 		} else if (errno != ENXIO) {
1777 			dhcpmsg(MSG_ERR,
1778 			    "remove_v6_strays: SIOCLIFREMOVEIF %s",
1779 			    lifrp->lifr_name);
1780 		}
1781 	}
1782 	free(lifc.lifc_buf);
1783 }
1784