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