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