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