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