10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52157Sdh155122  * Common Development and Distribution License (the "License").
62157Sdh155122  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
225978Smeem  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <sys/types.h>
270Sstevel@tonic-gate #include <sys/socket.h>
280Sstevel@tonic-gate #include <net/if.h>
290Sstevel@tonic-gate #include <stdlib.h>
300Sstevel@tonic-gate #include <sys/sockio.h>
310Sstevel@tonic-gate #include <netinet/in.h>
320Sstevel@tonic-gate #include <netinet/dhcp.h>
330Sstevel@tonic-gate #include <string.h>
340Sstevel@tonic-gate #include <unistd.h>
353431Scarlsonj #include <search.h>
363431Scarlsonj #include <libdevinfo.h>
374456Sss150715 #include <libdlpi.h>
380Sstevel@tonic-gate #include <netinet/if_ether.h>
393431Scarlsonj #include <arpa/inet.h>
400Sstevel@tonic-gate #include <dhcpmsg.h>
413431Scarlsonj #include <dhcp_inittab.h>
420Sstevel@tonic-gate 
433431Scarlsonj #include "agent.h"
440Sstevel@tonic-gate #include "interface.h"
450Sstevel@tonic-gate #include "util.h"
460Sstevel@tonic-gate #include "packet.h"
470Sstevel@tonic-gate #include "states.h"
483431Scarlsonj 
493431Scarlsonj dhcp_pif_t *v4root;
503431Scarlsonj dhcp_pif_t *v6root;
513431Scarlsonj 
523431Scarlsonj static uint_t cached_v4_max_mtu, cached_v6_max_mtu;
530Sstevel@tonic-gate 
540Sstevel@tonic-gate /*
553431Scarlsonj  * Interface flags to watch: things that should be under our direct control.
560Sstevel@tonic-gate  */
574823Sseb #define	DHCP_IFF_WATCH	(IFF_DHCPRUNNING | IFF_DEPRECATED | IFF_ADDRCONF | \
584823Sseb 	IFF_TEMPORARY)
590Sstevel@tonic-gate 
603431Scarlsonj static void clear_lif_dhcp(dhcp_lif_t *);
610Sstevel@tonic-gate 
620Sstevel@tonic-gate /*
633431Scarlsonj  * insert_pif(): creates a new physical interface structure and chains it on
643431Scarlsonj  *		 the list.  Initializes state that remains consistent across
653431Scarlsonj  *		 all use of the physical interface entry.
660Sstevel@tonic-gate  *
673431Scarlsonj  *   input: const char *: the name of the physical interface
683431Scarlsonj  *	    boolean_t: if B_TRUE, this is DHCPv6
693431Scarlsonj  *	    int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
700Sstevel@tonic-gate  *		   error code with the reason why
713431Scarlsonj  *  output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure
720Sstevel@tonic-gate  */
730Sstevel@tonic-gate 
743431Scarlsonj dhcp_pif_t *
753431Scarlsonj insert_pif(const char *pname, boolean_t isv6, int *error)
760Sstevel@tonic-gate {
773431Scarlsonj 	dhcp_pif_t *pif;
783431Scarlsonj 	struct lifreq lifr;
795381Smeem 	dlpi_handle_t dh = NULL;
805978Smeem 	int fd = isv6 ? v6_sock_fd : v4_sock_fd;
810Sstevel@tonic-gate 
823431Scarlsonj 	if ((pif = calloc(1, sizeof (*pif))) == NULL) {
833431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for "
843431Scarlsonj 		    "%s", pname);
853431Scarlsonj 		*error = DHCP_IPC_E_MEMORY;
860Sstevel@tonic-gate 		return (NULL);
870Sstevel@tonic-gate 	}
880Sstevel@tonic-gate 
893431Scarlsonj 	pif->pif_isv6 = isv6;
903431Scarlsonj 	pif->pif_hold_count = 1;
913431Scarlsonj 	pif->pif_running = B_TRUE;
923431Scarlsonj 
933431Scarlsonj 	if (strlcpy(pif->pif_name, pname, LIFNAMSIZ) >= LIFNAMSIZ) {
943431Scarlsonj 		dhcpmsg(MSG_ERROR, "insert_pif: interface name %s is too long",
953431Scarlsonj 		    pname);
963431Scarlsonj 		*error = DHCP_IPC_E_INVIF;
973431Scarlsonj 		goto failure;
983431Scarlsonj 	}
993431Scarlsonj 
1005978Smeem 	/*
1015978Smeem 	 * This is a bit gross, but IP has a confused interface.  We must
1025978Smeem 	 * assume that the zeroth LIF is plumbed, and must query there to get
1035978Smeem 	 * the interface index number.
1045978Smeem 	 */
1055978Smeem 	(void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ);
1065978Smeem 
1075978Smeem 	if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
1085978Smeem 		*error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
1095978Smeem 		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname);
1105978Smeem 		goto failure;
1115978Smeem 	}
1125978Smeem 	pif->pif_index = lifr.lifr_index;
1133431Scarlsonj 
1145978Smeem 	if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) {
1155978Smeem 		*error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
1165978Smeem 		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFMTU for %s", pname);
1175978Smeem 		goto failure;
1185978Smeem 	}
1195978Smeem 	pif->pif_max = lifr.lifr_mtu;
1203431Scarlsonj 
1215978Smeem 	if (pif->pif_max < DHCP_DEF_MAX_SIZE) {
1225978Smeem 		dhcpmsg(MSG_ERROR, "insert_pif: MTU of %s is too small to "
1235978Smeem 		    "support DHCP (%u < %u)", pname, pif->pif_max,
1245978Smeem 		    DHCP_DEF_MAX_SIZE);
1255978Smeem 		*error = DHCP_IPC_E_INVIF;
1265978Smeem 		goto failure;
1275978Smeem 	}
1285978Smeem 
1295978Smeem 	/*
1305978Smeem 	 * For IPv4, use DLPI to determine the hardware type, hardware
1315978Smeem 	 * address, and hardware address length.
1325978Smeem 	 */
1335978Smeem 	if (!isv6) {
1345978Smeem 		int		rc;
1355978Smeem 		dlpi_info_t	dlinfo;
1365978Smeem 
1374456Sss150715 		if ((rc = dlpi_open(pname, &dh, 0)) != DLPI_SUCCESS) {
1384456Sss150715 			dhcpmsg(MSG_ERROR, "insert_pif: dlpi_open: %s",
1394456Sss150715 			    dlpi_strerror(rc));
1404456Sss150715 			*error = DHCP_IPC_E_INVIF;
1414456Sss150715 			goto failure;
1424456Sss150715 		}
1434456Sss150715 
1444456Sss150715 		if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) {
1454456Sss150715 			dhcpmsg(MSG_ERROR, "insert_pif: dlpi_bind: %s",
1464456Sss150715 			    dlpi_strerror(rc));
1473431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
1483431Scarlsonj 			goto failure;
1493431Scarlsonj 		}
1503431Scarlsonj 
1515978Smeem 		if ((rc = dlpi_info(dh, &dlinfo, 0)) != DLPI_SUCCESS) {
1524456Sss150715 			dhcpmsg(MSG_ERROR, "insert_pif: dlpi_info: %s",
1534456Sss150715 			    dlpi_strerror(rc));
1544456Sss150715 			*error = DHCP_IPC_E_INVIF;
1554456Sss150715 			goto failure;
1564456Sss150715 		}
1574456Sss150715 
1584456Sss150715 		pif->pif_hwtype = dlpi_arptype(dlinfo.di_mactype);
1594456Sss150715 		pif->pif_hwlen  = dlinfo.di_physaddrlen;
1603431Scarlsonj 
1615381Smeem 		dhcpmsg(MSG_DEBUG, "insert_pif: %s: hwtype %d, hwlen %d",
1625381Smeem 		    pname, pif->pif_hwtype, pif->pif_hwlen);
1633431Scarlsonj 
1643431Scarlsonj 		if (pif->pif_hwlen > 0) {
1653431Scarlsonj 			pif->pif_hwaddr = malloc(pif->pif_hwlen);
1663431Scarlsonj 			if (pif->pif_hwaddr == NULL) {
1673431Scarlsonj 				dhcpmsg(MSG_ERR, "insert_pif: cannot allocate "
1683431Scarlsonj 				    "pif_hwaddr for %s", pname);
1693431Scarlsonj 				*error = DHCP_IPC_E_MEMORY;
1703431Scarlsonj 				goto failure;
1713431Scarlsonj 			}
1725381Smeem 			(void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr,
1735381Smeem 			    pif->pif_hwlen);
1743431Scarlsonj 		}
1753431Scarlsonj 
1765381Smeem 		dlpi_close(dh);
1775381Smeem 		dh = NULL;
1783431Scarlsonj 	}
1793431Scarlsonj 
1803431Scarlsonj 	insque(pif, isv6 ? &v6root : &v4root);
1813431Scarlsonj 
1823431Scarlsonj 	return (pif);
1833431Scarlsonj failure:
1845381Smeem 	if (dh != NULL)
1855381Smeem 		dlpi_close(dh);
1863431Scarlsonj 	release_pif(pif);
1873431Scarlsonj 	return (NULL);
1883431Scarlsonj }
1893431Scarlsonj 
1903431Scarlsonj /*
1913431Scarlsonj  * hold_pif(): acquire a hold on a physical interface structure.
1923431Scarlsonj  *
1933431Scarlsonj  *   input: dhcp_pif_t *: a pointer to the PIF structure
1943431Scarlsonj  *  output: none
1953431Scarlsonj  */
1963431Scarlsonj 
1973431Scarlsonj void
1983431Scarlsonj hold_pif(dhcp_pif_t *pif)
1993431Scarlsonj {
2003431Scarlsonj 	pif->pif_hold_count++;
2013431Scarlsonj 	dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name,
2023431Scarlsonj 	    pif->pif_hold_count);
2033431Scarlsonj }
2043431Scarlsonj 
2053431Scarlsonj /*
2063431Scarlsonj  * release_pif(): release a hold on a physical interface structure; will
2073431Scarlsonj  *		  destroy the structure on the last hold removed.
2083431Scarlsonj  *
2093431Scarlsonj  *   input: dhcp_pif_t *: a pointer to the PIF structure
2103431Scarlsonj  *  output: none
2113431Scarlsonj  */
2123431Scarlsonj 
2133431Scarlsonj void
2143431Scarlsonj release_pif(dhcp_pif_t *pif)
2153431Scarlsonj {
2163431Scarlsonj 	if (pif->pif_hold_count == 0) {
2173431Scarlsonj 		dhcpmsg(MSG_CRIT, "release_pif: extraneous release");
2183431Scarlsonj 		return;
2193431Scarlsonj 	}
2203431Scarlsonj 
2213431Scarlsonj 	if (--pif->pif_hold_count == 0) {
2223431Scarlsonj 		dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s",
2233431Scarlsonj 		    pif->pif_name);
2243431Scarlsonj 
2253431Scarlsonj 		remque(pif);
2263431Scarlsonj 		free(pif->pif_hwaddr);
2273431Scarlsonj 		free(pif);
2283431Scarlsonj 	} else {
2293431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u",
2303431Scarlsonj 		    pif->pif_name, pif->pif_hold_count);
2313431Scarlsonj 	}
2323431Scarlsonj }
2333431Scarlsonj 
2343431Scarlsonj /*
2353431Scarlsonj  * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and
2363431Scarlsonj  *			   previous PIF pointer (or NULL for list start).
2373431Scarlsonj  *			   Caller is expected to iterate through all
2383431Scarlsonj  *			   potential matches to find interface of interest.
2393431Scarlsonj  *
2403431Scarlsonj  *   input: uint16_t: the interface index (truncated)
2413431Scarlsonj  *	    dhcp_pif_t *: the previous PIF, or NULL for list start
2423431Scarlsonj  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
2433431Scarlsonj  *  output: dhcp_pif_t *: the next matching PIF, or NULL if not found
2443431Scarlsonj  *    note: This operates using the 'truncated' (16-bit) ifindex as seen by
2453431Scarlsonj  *	    routing socket clients.  The value stored in pif_index is the
2463431Scarlsonj  *	    32-bit ifindex from the ioctl interface.
2473431Scarlsonj  */
2483431Scarlsonj 
2493431Scarlsonj dhcp_pif_t *
2503431Scarlsonj lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6)
2513431Scarlsonj {
2523431Scarlsonj 	if (pif == NULL)
2533431Scarlsonj 		pif = isv6 ? v6root : v4root;
2543431Scarlsonj 	else
2553431Scarlsonj 		pif = pif->pif_next;
2563431Scarlsonj 
2573431Scarlsonj 	for (; pif != NULL; pif = pif->pif_next) {
2583431Scarlsonj 		if ((pif->pif_index & 0xffff) == ifindex)
2593431Scarlsonj 			break;
2603431Scarlsonj 	}
2613431Scarlsonj 
2623431Scarlsonj 	return (pif);
2633431Scarlsonj }
2643431Scarlsonj 
2653431Scarlsonj /*
2663431Scarlsonj  * lookup_pif_by_name(): Looks up a physical interface entry given a name.
2673431Scarlsonj  *
2683431Scarlsonj  *   input: const char *: the physical interface name
2693431Scarlsonj  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
2703431Scarlsonj  *  output: dhcp_pif_t *: the matching PIF, or NULL if not found
2713431Scarlsonj  */
2723431Scarlsonj 
2733431Scarlsonj dhcp_pif_t *
2743431Scarlsonj lookup_pif_by_name(const char *pname, boolean_t isv6)
2753431Scarlsonj {
2763431Scarlsonj 	dhcp_pif_t *pif;
2773431Scarlsonj 
2783431Scarlsonj 	pif = isv6 ? v6root : v4root;
2793431Scarlsonj 
2803431Scarlsonj 	for (; pif != NULL; pif = pif->pif_next) {
2813431Scarlsonj 		if (strcmp(pif->pif_name, pname) == 0)
2823431Scarlsonj 			break;
2833431Scarlsonj 	}
2843431Scarlsonj 
2853431Scarlsonj 	return (pif);
2863431Scarlsonj }
2873431Scarlsonj 
2883431Scarlsonj /*
2893431Scarlsonj  * pif_status(): update the physical interface up/down status.
2903431Scarlsonj  *
2915381Smeem  *   input: dhcp_pif_t *: the physical interface to be updated
2923431Scarlsonj  *	    boolean_t: B_TRUE if the interface is going up
2933431Scarlsonj  *  output: none
2943431Scarlsonj  */
2953431Scarlsonj 
2963431Scarlsonj void
2973431Scarlsonj pif_status(dhcp_pif_t *pif, boolean_t isup)
2983431Scarlsonj {
2993431Scarlsonj 	dhcp_lif_t *lif;
3003431Scarlsonj 	dhcp_smach_t *dsmp;
3013431Scarlsonj 
3023431Scarlsonj 	pif->pif_running = isup;
3034516Scarlsonj 	dhcpmsg(MSG_DEBUG, "interface %s has %s", pif->pif_name,
3043431Scarlsonj 	    isup ? "come back up" : "gone down");
3053431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
3063431Scarlsonj 		for (dsmp = lif->lif_smachs; dsmp != NULL;
3073431Scarlsonj 		    dsmp = dsmp->dsm_next) {
3083431Scarlsonj 			if (isup)
3093431Scarlsonj 				refresh_smach(dsmp);
3103431Scarlsonj 			else
3113431Scarlsonj 				remove_default_routes(dsmp);
3123431Scarlsonj 		}
3133431Scarlsonj 	}
3143431Scarlsonj }
3153431Scarlsonj 
3163431Scarlsonj /* Helper for insert_lif: extract addresses as defined */
3173431Scarlsonj #define	ASSIGN_ADDR(v4, v6, lf) \
3183431Scarlsonj 	if (pif->pif_isv6) { \
3193431Scarlsonj 		lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \
3203431Scarlsonj 	} else { \
3213431Scarlsonj 		lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \
3223431Scarlsonj 	}
3233431Scarlsonj 
3243431Scarlsonj /*
3253431Scarlsonj  * insert_lif(): Creates a new logical interface structure and chains it on
3263431Scarlsonj  *		 the list for a given physical interface.  Initializes state
3273431Scarlsonj  *		 that remains consistent across all use of the logical
3283431Scarlsonj  *		 interface entry.  Caller's PIF hold is transferred to the
3293431Scarlsonj  *		 LIF on success, and is dropped on failure.
3303431Scarlsonj  *
3313431Scarlsonj  *   input: dhcp_pif_t *: pointer to the physical interface for this LIF
3323431Scarlsonj  *	    const char *: the name of the logical interface
3333431Scarlsonj  *	    int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
3343431Scarlsonj  *		   error code with the reason why
3353431Scarlsonj  *  output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure
3363431Scarlsonj  */
3373431Scarlsonj 
3383431Scarlsonj dhcp_lif_t *
3393431Scarlsonj insert_lif(dhcp_pif_t *pif, const char *lname, int *error)
3403431Scarlsonj {
3413431Scarlsonj 	dhcp_lif_t *lif;
3423431Scarlsonj 	int fd;
3433431Scarlsonj 	struct lifreq lifr;
3443431Scarlsonj 
3453431Scarlsonj 	if ((lif = calloc(1, sizeof (*lif))) == NULL) {
3463431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for "
3473431Scarlsonj 		    "%s", lname);
3480Sstevel@tonic-gate 		*error = DHCP_IPC_E_MEMORY;
3490Sstevel@tonic-gate 		return (NULL);
3500Sstevel@tonic-gate 	}
3510Sstevel@tonic-gate 
3523431Scarlsonj 	lif->lif_sock_ip_fd = -1;
3535381Smeem 	lif->lif_packet_id = -1;
3543431Scarlsonj 	lif->lif_iaid_id = -1;
3553431Scarlsonj 	lif->lif_hold_count = 1;
3563431Scarlsonj 	lif->lif_pif = pif;
3573431Scarlsonj 	lif->lif_removed = B_TRUE;
3583431Scarlsonj 	init_timer(&lif->lif_preferred, 0);
3593431Scarlsonj 	init_timer(&lif->lif_expire, 0);
3600Sstevel@tonic-gate 
3613431Scarlsonj 	if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) {
3623431Scarlsonj 		dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long",
3633431Scarlsonj 		    lname);
3640Sstevel@tonic-gate 		*error = DHCP_IPC_E_INVIF;
3650Sstevel@tonic-gate 		goto failure;
3660Sstevel@tonic-gate 	}
3670Sstevel@tonic-gate 
3683431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ);
3693431Scarlsonj 
3703431Scarlsonj 	fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
3710Sstevel@tonic-gate 
3723431Scarlsonj 	if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1)
3733431Scarlsonj 		lif->lif_max = 1024;
3743431Scarlsonj 	else
3753431Scarlsonj 		lif->lif_max = lifr.lifr_mtu;
3760Sstevel@tonic-gate 
3773431Scarlsonj 	if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) {
3783431Scarlsonj 		if (errno == ENXIO)
3793431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
3803431Scarlsonj 		else
3813431Scarlsonj 			*error = DHCP_IPC_E_INT;
3823431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname);
3830Sstevel@tonic-gate 		goto failure;
3840Sstevel@tonic-gate 	}
3853431Scarlsonj 	ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr);
3860Sstevel@tonic-gate 
3873431Scarlsonj 	if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) {
3882546Scarlsonj 		if (errno == ENXIO)
3892546Scarlsonj 			*error = DHCP_IPC_E_INVIF;
3902546Scarlsonj 		else
3912546Scarlsonj 			*error = DHCP_IPC_E_INT;
3923431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname);
3933431Scarlsonj 		goto failure;
3943431Scarlsonj 	}
3953431Scarlsonj 	ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr);
3963431Scarlsonj 
3973431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
3983431Scarlsonj 		*error = DHCP_IPC_E_INT;
3993431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname);
4002546Scarlsonj 		goto failure;
4012546Scarlsonj 	}
4023431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
4033431Scarlsonj 
4043431Scarlsonj 	/*
4053431Scarlsonj 	 * If we've just detected the interface going up or down, then signal
4063431Scarlsonj 	 * an appropriate action.  There may be other state machines here.
4073431Scarlsonj 	 */
4083431Scarlsonj 	if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) {
4093431Scarlsonj 		pif_status(pif, B_TRUE);
4103431Scarlsonj 	} else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) {
4113431Scarlsonj 		pif_status(pif, B_FALSE);
4123431Scarlsonj 	}
4133431Scarlsonj 
4143431Scarlsonj 	if (lifr.lifr_flags & IFF_POINTOPOINT) {
4153431Scarlsonj 		if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) {
4163431Scarlsonj 			*error = DHCP_IPC_E_INT;
4173431Scarlsonj 			dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s",
4183431Scarlsonj 			    lname);
4193431Scarlsonj 			goto failure;
4203431Scarlsonj 		}
4213431Scarlsonj 		ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr);
4223431Scarlsonj 	} else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) {
4233431Scarlsonj 		if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) {
4243431Scarlsonj 			*error = DHCP_IPC_E_INT;
4253431Scarlsonj 			dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s",
4263431Scarlsonj 			    lname);
4273431Scarlsonj 			goto failure;
4283431Scarlsonj 		}
4293431Scarlsonj 		lif->lif_broadcast =
4303431Scarlsonj 		    ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr.
4313431Scarlsonj 		    s_addr;
4323431Scarlsonj 	}
4333431Scarlsonj 
4343431Scarlsonj 	if (pif->pif_isv6)
4353431Scarlsonj 		cached_v6_max_mtu = 0;
4363431Scarlsonj 	else
4373431Scarlsonj 		cached_v4_max_mtu = 0;
4383431Scarlsonj 
4393431Scarlsonj 	lif->lif_removed = B_FALSE;
4403431Scarlsonj 	insque(lif, &pif->pif_lifs);
4413431Scarlsonj 
4423431Scarlsonj 	return (lif);
4433431Scarlsonj 
4443431Scarlsonj failure:
4453431Scarlsonj 	release_lif(lif);
4463431Scarlsonj 	return (NULL);
4473431Scarlsonj }
4483431Scarlsonj 
4493431Scarlsonj /*
4503431Scarlsonj  * hold_lif(): acquire a hold on a logical interface structure.
4513431Scarlsonj  *
4523431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
4533431Scarlsonj  *  output: none
4543431Scarlsonj  */
4553431Scarlsonj 
4563431Scarlsonj void
4573431Scarlsonj hold_lif(dhcp_lif_t *lif)
4583431Scarlsonj {
4593431Scarlsonj 	lif->lif_hold_count++;
4603431Scarlsonj 	dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name,
4613431Scarlsonj 	    lif->lif_hold_count);
4623431Scarlsonj }
4633431Scarlsonj 
4643431Scarlsonj /*
4653431Scarlsonj  * release_lif(): release a hold on a logical interface structure; will
4663431Scarlsonj  *		  destroy the structure on the last hold removed.
4673431Scarlsonj  *
4683431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
4693431Scarlsonj  *  output: none
4703431Scarlsonj  */
4713431Scarlsonj 
4723431Scarlsonj void
4733431Scarlsonj release_lif(dhcp_lif_t *lif)
4743431Scarlsonj {
4753431Scarlsonj 	if (lif->lif_hold_count == 0) {
4763431Scarlsonj 		dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s",
4773431Scarlsonj 		    lif->lif_name);
4783431Scarlsonj 		return;
4793431Scarlsonj 	}
4803431Scarlsonj 
4813431Scarlsonj 	if (lif->lif_hold_count == 1 && !lif->lif_removed) {
4823431Scarlsonj 		unplumb_lif(lif);
4833431Scarlsonj 		return;
4843431Scarlsonj 	}
4853431Scarlsonj 
4863431Scarlsonj 	if (--lif->lif_hold_count == 0) {
4873431Scarlsonj 		dhcp_pif_t *pif;
4883431Scarlsonj 
4893431Scarlsonj 		dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s",
4903431Scarlsonj 		    lif->lif_name);
4913431Scarlsonj 
4923431Scarlsonj 		if (lif->lif_lease != NULL)
4933431Scarlsonj 			dhcpmsg(MSG_CRIT,
4943431Scarlsonj 			    "release_lif: still holding lease at last hold!");
4953431Scarlsonj 		close_ip_lif(lif);
4963431Scarlsonj 		pif = lif->lif_pif;
4973431Scarlsonj 		if (pif->pif_isv6)
4983431Scarlsonj 			cached_v6_max_mtu = 0;
4993431Scarlsonj 		else
5003431Scarlsonj 			cached_v4_max_mtu = 0;
5013431Scarlsonj 		release_pif(pif);
5023431Scarlsonj 		free(lif);
5033431Scarlsonj 	} else {
5043431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u",
5053431Scarlsonj 		    lif->lif_name, lif->lif_hold_count);
5063431Scarlsonj 	}
5073431Scarlsonj }
5083431Scarlsonj 
5093431Scarlsonj /*
5103431Scarlsonj  * remove_lif(): remove a logical interface from its PIF and lease (if any) and
5113431Scarlsonj  *		 the lease's hold on the LIF.  Assumes that we did not plumb
5123431Scarlsonj  *		 the interface.
5133431Scarlsonj  *
5143431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
5153431Scarlsonj  *  output: none
5163431Scarlsonj  */
5173431Scarlsonj 
5183431Scarlsonj void
5193431Scarlsonj remove_lif(dhcp_lif_t *lif)
5203431Scarlsonj {
5213431Scarlsonj 	if (lif->lif_plumbed) {
5223431Scarlsonj 		dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s",
5233431Scarlsonj 		    lif->lif_name);
5243431Scarlsonj 		return;
5253431Scarlsonj 	}
5263431Scarlsonj 	if (lif->lif_removed) {
5273431Scarlsonj 		dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s",
5283431Scarlsonj 		    lif->lif_name);
5293431Scarlsonj 	} else {
5303431Scarlsonj 		dhcp_lif_t *lifnext;
5313431Scarlsonj 		dhcp_lease_t *dlp;
5323431Scarlsonj 
5333431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name);
5343431Scarlsonj 		lif->lif_removed = B_TRUE;
5353431Scarlsonj 		lifnext = lif->lif_next;
5363431Scarlsonj 		clear_lif_dhcp(lif);
5373431Scarlsonj 		cancel_lif_timers(lif);
5383431Scarlsonj 		if (lif->lif_iaid_id != -1 &&
5393431Scarlsonj 		    iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) {
5403431Scarlsonj 			lif->lif_iaid_id = -1;
5413431Scarlsonj 			release_lif(lif);
5423431Scarlsonj 		}
5433431Scarlsonj 
5443431Scarlsonj 		/* Remove from PIF list */
5453431Scarlsonj 		remque(lif);
5463431Scarlsonj 
5473431Scarlsonj 		/* If we were part of a lease, then remove ourselves */
5483431Scarlsonj 		if ((dlp = lif->lif_lease) != NULL) {
5493431Scarlsonj 			if (--dlp->dl_nlifs == 0)
5503431Scarlsonj 				dlp->dl_lifs = NULL;
5513431Scarlsonj 			else if (dlp->dl_lifs == lif)
5523431Scarlsonj 				dlp->dl_lifs = lifnext;
5533431Scarlsonj 			if (lif->lif_declined != NULL) {
5543431Scarlsonj 				dlp->dl_smach->dsm_lif_down--;
5553431Scarlsonj 				lif->lif_declined = NULL;
5563431Scarlsonj 			}
5573431Scarlsonj 			lif->lif_lease = NULL;
5583431Scarlsonj 			release_lif(lif);
5593431Scarlsonj 		}
5603431Scarlsonj 	}
5613431Scarlsonj }
5623431Scarlsonj 
5633431Scarlsonj /*
5643431Scarlsonj  * lookup_lif_by_name(): Looks up a logical interface entry given a name and
5653431Scarlsonj  *			 a physical interface.
5663431Scarlsonj  *
5673431Scarlsonj  *   input: const char *: the logical interface name
5683431Scarlsonj  *	    const dhcp_pif_t *: the physical interface
5693431Scarlsonj  *  output: dhcp_lif_t *: the matching LIF, or NULL if not found
5703431Scarlsonj  */
5713431Scarlsonj 
5723431Scarlsonj dhcp_lif_t *
5733431Scarlsonj lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif)
5743431Scarlsonj {
5753431Scarlsonj 	dhcp_lif_t *lif;
5763431Scarlsonj 
5773431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
5783431Scarlsonj 		if (strcmp(lif->lif_name, lname) == 0)
5793431Scarlsonj 			break;
5803431Scarlsonj 	}
5813431Scarlsonj 
5823431Scarlsonj 	return (lif);
5833431Scarlsonj }
5843431Scarlsonj 
5853431Scarlsonj /*
5863431Scarlsonj  * checkaddr(): checks if the given address is still set on the given LIF
5873431Scarlsonj  *
5883431Scarlsonj  *   input: const dhcp_lif_t *: the LIF to check
5893431Scarlsonj  *	    int: the address to look up on the interface (ioctl)
5903431Scarlsonj  *	    const in6_addr_t *: the address to compare to
5913431Scarlsonj  *	    const char *: name of the address for logging purposes
5923431Scarlsonj  *  output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
5933431Scarlsonj  */
5943431Scarlsonj 
5953431Scarlsonj static boolean_t
5963431Scarlsonj checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr,
5973431Scarlsonj     const char *aname)
5983431Scarlsonj {
5993431Scarlsonj 	boolean_t isv6;
6003431Scarlsonj 	int fd;
6013431Scarlsonj 	struct lifreq lifr;
6025381Smeem 	char abuf1[INET6_ADDRSTRLEN];
6035381Smeem 	char abuf2[INET6_ADDRSTRLEN];
6043431Scarlsonj 
6053431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
6063431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
6073431Scarlsonj 
6083431Scarlsonj 	isv6 = lif->lif_pif->pif_isv6;
6093431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
6103431Scarlsonj 
6113431Scarlsonj 	if (ioctl(fd, ioccmd, &lifr) == -1) {
6123431Scarlsonj 		if (errno == ENXIO) {
6133431Scarlsonj 			dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone",
6143431Scarlsonj 			    lif->lif_name);
6153431Scarlsonj 			return (B_FALSE);
6163431Scarlsonj 		}
6173431Scarlsonj 		dhcpmsg(MSG_DEBUG,
6183431Scarlsonj 		    "checkaddr: ignoring ioctl error on %s %x: %s",
6193431Scarlsonj 		    lif->lif_name, ioccmd, strerror(errno));
6203431Scarlsonj 	} else if (isv6) {
6213431Scarlsonj 		struct sockaddr_in6 *sin6 =
6223431Scarlsonj 		    (struct sockaddr_in6 *)&lifr.lifr_addr;
6233431Scarlsonj 
6243431Scarlsonj 		if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) {
6253431Scarlsonj 			dhcpmsg(MSG_WARNING,
6265381Smeem 			    "checkaddr: expected %s %s on %s, have %s", aname,
6275381Smeem 			    inet_ntop(AF_INET6, addr, abuf1, sizeof (abuf1)),
6285381Smeem 			    lif->lif_name, inet_ntop(AF_INET6, &sin6->sin6_addr,
6295381Smeem 			    abuf2, sizeof (abuf2)));
6303431Scarlsonj 			return (B_FALSE);
6313431Scarlsonj 		}
6323431Scarlsonj 	} else {
6333431Scarlsonj 		struct sockaddr_in *sinp =
6343431Scarlsonj 		    (struct sockaddr_in *)&lifr.lifr_addr;
6353431Scarlsonj 		ipaddr_t v4addr;
6363431Scarlsonj 
6373431Scarlsonj 		IN6_V4MAPPED_TO_IPADDR(addr, v4addr);
6383431Scarlsonj 		if (sinp->sin_addr.s_addr != v4addr) {
6393431Scarlsonj 			dhcpmsg(MSG_WARNING,
6405381Smeem 			    "checkaddr: expected %s %s on %s, have %s", aname,
6415381Smeem 			    inet_ntop(AF_INET, &v4addr, abuf1, sizeof (abuf1)),
6425381Smeem 			    lif->lif_name, inet_ntop(AF_INET, &sinp->sin_addr,
6435381Smeem 			    abuf2, sizeof (abuf2)));
6443431Scarlsonj 			return (B_FALSE);
6453431Scarlsonj 		}
6463431Scarlsonj 	}
6473431Scarlsonj 	return (B_TRUE);
6483431Scarlsonj }
6493431Scarlsonj 
6503431Scarlsonj /*
6513431Scarlsonj  * verify_lif(): verifies than a LIF is still valid (i.e., has not been
6523431Scarlsonj  *		 explicitly or implicitly dropped or released)
6533431Scarlsonj  *
6543431Scarlsonj  *   input: const dhcp_lif_t *: the LIF to verify
6553431Scarlsonj  *  output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise
6563431Scarlsonj  */
6573431Scarlsonj 
6583431Scarlsonj boolean_t
6593431Scarlsonj verify_lif(const dhcp_lif_t *lif)
6603431Scarlsonj {
6613431Scarlsonj 	boolean_t isv6;
6623431Scarlsonj 	int fd;
6633431Scarlsonj 	struct lifreq lifr;
6643431Scarlsonj 
6653431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
6663431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
6673431Scarlsonj 
6683431Scarlsonj 	isv6 = lif->lif_pif->pif_isv6;
6693431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
6703431Scarlsonj 
6713431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
6724106Scarlsonj 		if (errno != ENXIO) {
6734106Scarlsonj 			dhcpmsg(MSG_ERR,
6744106Scarlsonj 			    "verify_lif: SIOCGLIFFLAGS failed on %s",
6754106Scarlsonj 			    lif->lif_name);
6764106Scarlsonj 		}
6773431Scarlsonj 		return (B_FALSE);
6783431Scarlsonj 	}
6793431Scarlsonj 
6803431Scarlsonj 	/*
6813431Scarlsonj 	 * If important flags have changed, then abandon the interface.
6823431Scarlsonj 	 */
6833431Scarlsonj 	if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) {
6843431Scarlsonj 		dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: "
6853431Scarlsonj 		    "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags,
6863431Scarlsonj 		    lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) &
6873431Scarlsonj 		    DHCP_IFF_WATCH);
6883431Scarlsonj 		return (B_FALSE);
6893431Scarlsonj 	}
6903431Scarlsonj 
6913431Scarlsonj 	/*
6923431Scarlsonj 	 * Special case: if the interface has gone down as a duplicate, then
6933431Scarlsonj 	 * this alone does _not_ mean that we're abandoning it just yet.  Allow
6943431Scarlsonj 	 * the state machine to handle this normally by trying to get a new
6953431Scarlsonj 	 * lease.
6963431Scarlsonj 	 */
6973431Scarlsonj 	if ((lifr.lifr_flags & (IFF_UP|IFF_DUPLICATE)) == IFF_DUPLICATE) {
6983431Scarlsonj 		dhcpmsg(MSG_DEBUG, "verify_lif: duplicate address on %s",
6993431Scarlsonj 		    lif->lif_name);
7003431Scarlsonj 		return (B_TRUE);
7013431Scarlsonj 	}
7023431Scarlsonj 
7033431Scarlsonj 	/*
7043431Scarlsonj 	 * If the user has torn down or started up the interface manually, then
7053431Scarlsonj 	 * abandon the lease.
7063431Scarlsonj 	 */
7073431Scarlsonj 	if ((lif->lif_flags ^ lifr.lifr_flags) & IFF_UP) {
7083431Scarlsonj 		dhcpmsg(MSG_DEBUG, "verify_lif: user has %s %s",
7093431Scarlsonj 		    lifr.lifr_flags & IFF_UP ? "started up" : "shut down",
7103431Scarlsonj 		    lif->lif_name);
7113431Scarlsonj 		return (B_FALSE);
7123431Scarlsonj 	}
7133431Scarlsonj 
7143431Scarlsonj 	/*
7153431Scarlsonj 	 * Check for delete and recreate.
7163431Scarlsonj 	 */
7173431Scarlsonj 	if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
7183431Scarlsonj 		dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed on %s",
7193431Scarlsonj 		    lif->lif_name);
7203431Scarlsonj 		return (B_FALSE);
7213431Scarlsonj 	}
7223431Scarlsonj 	if (lifr.lifr_index != lif->lif_pif->pif_index) {
7233431Scarlsonj 		dhcpmsg(MSG_DEBUG,
7243431Scarlsonj 		    "verify_lif: ifindex on %s changed: %u to %u",
7253431Scarlsonj 		    lif->lif_name, lif->lif_pif->pif_index, lifr.lifr_index);
7263431Scarlsonj 		return (B_FALSE);
7273431Scarlsonj 	}
7283431Scarlsonj 
7293431Scarlsonj 	/*
7303431Scarlsonj 	 * If the IP address, netmask, or broadcast address have changed, or
7313431Scarlsonj 	 * the interface has been unplumbed, then we act like there has been an
7323431Scarlsonj 	 * implicit drop.  (Note that the netmask is under DHCP control for
7333431Scarlsonj 	 * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast
7343431Scarlsonj 	 * addresses.)
7353431Scarlsonj 	 */
7362546Scarlsonj 
7373431Scarlsonj 	if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address"))
7383431Scarlsonj 		return (B_FALSE);
7393431Scarlsonj 
7403431Scarlsonj 	if (isv6) {
7413431Scarlsonj 		/*
7423431Scarlsonj 		 * If it's not point-to-point, we're done.  If it is, then
7433431Scarlsonj 		 * check the peer's address as well.
7443431Scarlsonj 		 */
7453431Scarlsonj 		return (!(lif->lif_flags & IFF_POINTOPOINT) ||
7463431Scarlsonj 		    checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer,
7473431Scarlsonj 		    "peer address"));
7483431Scarlsonj 	} else {
7493431Scarlsonj 		if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask,
7503431Scarlsonj 		    "netmask"))
7513431Scarlsonj 			return (B_FALSE);
7523431Scarlsonj 
7533431Scarlsonj 		return (checkaddr(lif,
7543431Scarlsonj 		    (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR :
7553431Scarlsonj 		    SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address"));
7563431Scarlsonj 	}
7573431Scarlsonj }
7583431Scarlsonj 
7593431Scarlsonj /*
7603431Scarlsonj  * canonize_lif(): puts the interface in a canonical (zeroed) form.  This is
7613431Scarlsonj  *		   used only on the "main" LIF for IPv4.  All other interfaces
7623431Scarlsonj  *		   are under dhcpagent control and are removed using
7633431Scarlsonj  *		   unplumb_lif().
7643431Scarlsonj  *
7653431Scarlsonj  *   input: dhcp_lif_t *: the interface to canonize
7665381Smeem  *	    boolean_t: only canonize lif if it's under DHCP control
7673431Scarlsonj  *  output: none
7683431Scarlsonj  */
7693431Scarlsonj 
7703431Scarlsonj static void
7715381Smeem canonize_lif(dhcp_lif_t *lif, boolean_t dhcponly)
7723431Scarlsonj {
7733431Scarlsonj 	boolean_t isv6;
7743431Scarlsonj 	int fd;
7753431Scarlsonj 	struct lifreq lifr;
7763431Scarlsonj 
7773431Scarlsonj 	/*
7783431Scarlsonj 	 * If there's nothing here, then don't touch the interface.  This can
7793431Scarlsonj 	 * happen when an already-canonized LIF is recanonized.
7803431Scarlsonj 	 */
7813431Scarlsonj 	if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr))
7823431Scarlsonj 		return;
7833431Scarlsonj 
7843431Scarlsonj 	isv6 = lif->lif_pif->pif_isv6;
7853431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s",
7863431Scarlsonj 	    isv6 ? 6 : 4, lif->lif_name);
7873431Scarlsonj 
7883431Scarlsonj 	lif->lif_v6addr = my_in6addr_any;
7893431Scarlsonj 	lif->lif_v6mask = my_in6addr_any;
7903431Scarlsonj 	lif->lif_v6peer = my_in6addr_any;
7913431Scarlsonj 
7923431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
7933431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
7943431Scarlsonj 
7953431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
7963431Scarlsonj 
7973431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
7984106Scarlsonj 		if (errno != ENXIO) {
7994106Scarlsonj 			dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s",
8004106Scarlsonj 			    lif->lif_name);
8014106Scarlsonj 		}
8023431Scarlsonj 		return;
8033431Scarlsonj 	}
8046637Smeem 	lif->lif_flags = lifr.lifr_flags;
8053431Scarlsonj 
8065381Smeem 	if (dhcponly && !(lifr.lifr_flags & IFF_DHCPRUNNING)) {
8073431Scarlsonj 		dhcpmsg(MSG_INFO,
8083431Scarlsonj 		    "canonize_lif: cannot clear %s; flags are %llx",
8093431Scarlsonj 		    lif->lif_name, lifr.lifr_flags);
8103431Scarlsonj 		return;
8113431Scarlsonj 	}
8123431Scarlsonj 
8133431Scarlsonj 	(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
8143431Scarlsonj 	if (isv6) {
8153431Scarlsonj 		struct sockaddr_in6 *sin6 =
8163431Scarlsonj 		    (struct sockaddr_in6 *)&lifr.lifr_addr;
8173431Scarlsonj 
8183431Scarlsonj 		sin6->sin6_family = AF_INET6;
8193431Scarlsonj 		sin6->sin6_addr = my_in6addr_any;
8203431Scarlsonj 	} else {
8213431Scarlsonj 		struct sockaddr_in *sinv =
8223431Scarlsonj 		    (struct sockaddr_in *)&lifr.lifr_addr;
8233431Scarlsonj 
8243431Scarlsonj 		sinv->sin_family = AF_INET;
8253431Scarlsonj 		sinv->sin_addr.s_addr = htonl(INADDR_ANY);
8263431Scarlsonj 	}
8273431Scarlsonj 
8283431Scarlsonj 	if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) {
8293431Scarlsonj 		dhcpmsg(MSG_ERR,
8303431Scarlsonj 		    "canonize_lif: can't clear local address on %s",
8313431Scarlsonj 		    lif->lif_name);
8323431Scarlsonj 	}
8333431Scarlsonj 
8343431Scarlsonj 	if (lif->lif_flags & IFF_POINTOPOINT) {
8353431Scarlsonj 		if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) {
8363431Scarlsonj 			dhcpmsg(MSG_ERR,
8373431Scarlsonj 			    "canonize_lif: can't clear remote address on %s",
8383431Scarlsonj 			    lif->lif_name);
8393431Scarlsonj 		}
8403431Scarlsonj 	} else if (!isv6) {
8413431Scarlsonj 		if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) {
8423431Scarlsonj 			dhcpmsg(MSG_ERR,
8433431Scarlsonj 			    "canonize_lif: can't clear broadcast address on %s",
8443431Scarlsonj 			    lif->lif_name);
8453431Scarlsonj 		}
8463431Scarlsonj 	}
847*8403SAnurag.Maskey@Sun.COM 
848*8403SAnurag.Maskey@Sun.COM 	/*
849*8403SAnurag.Maskey@Sun.COM 	 * Clear the netmask last as it has to be refetched after clearing.
850*8403SAnurag.Maskey@Sun.COM 	 * Netmask is under in.ndpd control with IPv6.
851*8403SAnurag.Maskey@Sun.COM 	 */
852*8403SAnurag.Maskey@Sun.COM 	if (!isv6) {
853*8403SAnurag.Maskey@Sun.COM 		/* Clear the netmask */
854*8403SAnurag.Maskey@Sun.COM 		if (ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) {
855*8403SAnurag.Maskey@Sun.COM 			dhcpmsg(MSG_ERR,
856*8403SAnurag.Maskey@Sun.COM 			    "canonize_lif: can't clear netmask on %s",
857*8403SAnurag.Maskey@Sun.COM 			    lif->lif_name);
858*8403SAnurag.Maskey@Sun.COM 		} else  {
859*8403SAnurag.Maskey@Sun.COM 			/*
860*8403SAnurag.Maskey@Sun.COM 			 * When the netmask is cleared, the kernel actually sets
861*8403SAnurag.Maskey@Sun.COM 			 * the netmask to 255.0.0.0.  So, refetch that netmask.
862*8403SAnurag.Maskey@Sun.COM 			 */
863*8403SAnurag.Maskey@Sun.COM 			if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) {
864*8403SAnurag.Maskey@Sun.COM 				dhcpmsg(MSG_ERR,
865*8403SAnurag.Maskey@Sun.COM 				    "canonize_lif: can't reload cleared "
866*8403SAnurag.Maskey@Sun.COM 				    "netmask on %s", lif->lif_name);
867*8403SAnurag.Maskey@Sun.COM 			} else {
868*8403SAnurag.Maskey@Sun.COM 				/* Refetch succeeded, update LIF */
869*8403SAnurag.Maskey@Sun.COM 				lif->lif_netmask =
870*8403SAnurag.Maskey@Sun.COM 				    ((struct sockaddr_in *)&lifr.lifr_addr)->
871*8403SAnurag.Maskey@Sun.COM 				    sin_addr.s_addr;
872*8403SAnurag.Maskey@Sun.COM 			}
873*8403SAnurag.Maskey@Sun.COM 		}
874*8403SAnurag.Maskey@Sun.COM 	}
8753431Scarlsonj }
8763431Scarlsonj 
8773431Scarlsonj /*
8783431Scarlsonj  * plumb_lif(): Adds the LIF to the system.  This is used for all
8793431Scarlsonj  *		DHCPv6-derived interfaces.  The returned LIF has a hold
8803431Scarlsonj  *		on it.
8813431Scarlsonj  *
8823431Scarlsonj  *   input: dhcp_lif_t *: the interface to unplumb
8833431Scarlsonj  *  output: none
8843431Scarlsonj  */
8853431Scarlsonj 
8863431Scarlsonj dhcp_lif_t *
8873431Scarlsonj plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr)
8883431Scarlsonj {
8893431Scarlsonj 	dhcp_lif_t *lif;
8903431Scarlsonj 	char abuf[INET6_ADDRSTRLEN];
8913431Scarlsonj 	struct lifreq lifr;
8923431Scarlsonj 	struct sockaddr_in6 *sin6;
8933431Scarlsonj 	int error;
8943431Scarlsonj 
8953431Scarlsonj 	(void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf));
8963431Scarlsonj 
8973431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
8983431Scarlsonj 		if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) {
8993431Scarlsonj 			dhcpmsg(MSG_ERR,
9003431Scarlsonj 			    "plumb_lif: entry for %s already exists!", abuf);
9013431Scarlsonj 			return (NULL);
9023431Scarlsonj 		}
9033431Scarlsonj 	}
9043431Scarlsonj 
9053431Scarlsonj 	/* First, create a new zero-address logical interface */
9063431Scarlsonj 	(void) memset(&lifr, 0, sizeof (lifr));
9073431Scarlsonj 	(void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name));
9083431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) {
9093431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name);
9103431Scarlsonj 		return (NULL);
9113431Scarlsonj 	}
9123431Scarlsonj 
9133431Scarlsonj 	/* Next, set the netmask to all ones */
9143431Scarlsonj 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
9153431Scarlsonj 	sin6->sin6_family = AF_INET6;
9163431Scarlsonj 	(void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr));
9173431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
9183431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s",
9193431Scarlsonj 		    lifr.lifr_name);
9200Sstevel@tonic-gate 		goto failure;
9210Sstevel@tonic-gate 	}
9220Sstevel@tonic-gate 
9233431Scarlsonj 	/* Now set the interface address */
9243431Scarlsonj 	sin6->sin6_addr = *addr;
9253431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
9263431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s",
9273431Scarlsonj 		    lifr.lifr_name, abuf);
9283431Scarlsonj 		goto failure;
9293431Scarlsonj 	}
9303431Scarlsonj 
9313431Scarlsonj 	/* Mark the interface up */
9323431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
9333431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s",
9343431Scarlsonj 		    lifr.lifr_name);
9353431Scarlsonj 		goto failure;
9363431Scarlsonj 	}
9373431Scarlsonj 	lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING;
9383431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
9393431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s",
9403431Scarlsonj 		    lifr.lifr_name);
9413431Scarlsonj 		goto failure;
9423431Scarlsonj 	}
9433431Scarlsonj 
9443431Scarlsonj 	/* Now we can create the internal LIF structure */
9453431Scarlsonj 	hold_pif(pif);
9463431Scarlsonj 	if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL)
9473431Scarlsonj 		goto failure;
9483431Scarlsonj 
9493431Scarlsonj 	dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf,
9503431Scarlsonj 	    lif->lif_name);
9513431Scarlsonj 	lif->lif_plumbed = B_TRUE;
9523431Scarlsonj 
9533431Scarlsonj 	return (lif);
9543431Scarlsonj 
9553431Scarlsonj failure:
9563431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
9573431Scarlsonj 	    errno != ENXIO) {
9583431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s",
9593431Scarlsonj 		    lifr.lifr_name);
9603431Scarlsonj 	}
9613431Scarlsonj 	return (NULL);
9623431Scarlsonj }
9633431Scarlsonj 
9643431Scarlsonj /*
9653431Scarlsonj  * unplumb_lif(): Removes the LIF from dhcpagent and the system.  This is used
9663431Scarlsonj  *		  for all interfaces configured by DHCP (those in leases).
9673431Scarlsonj  *
9683431Scarlsonj  *   input: dhcp_lif_t *: the interface to unplumb
9693431Scarlsonj  *  output: none
9703431Scarlsonj  */
9713431Scarlsonj 
9723431Scarlsonj void
9733431Scarlsonj unplumb_lif(dhcp_lif_t *lif)
9743431Scarlsonj {
9753431Scarlsonj 	dhcp_lease_t *dlp;
9763431Scarlsonj 
9773431Scarlsonj 	if (lif->lif_plumbed) {
9783431Scarlsonj 		struct lifreq lifr;
9793431Scarlsonj 
9803431Scarlsonj 		(void) memset(&lifr, 0, sizeof (lifr));
9813431Scarlsonj 		(void) strlcpy(lifr.lifr_name, lif->lif_name,
9823431Scarlsonj 		    sizeof (lifr.lifr_name));
9833431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
9843431Scarlsonj 		    errno != ENXIO) {
9853431Scarlsonj 			dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s",
9863431Scarlsonj 			    lif->lif_name);
9873431Scarlsonj 		}
9883431Scarlsonj 		lif->lif_plumbed = B_FALSE;
9893431Scarlsonj 	}
9905978Smeem 
9913431Scarlsonj 	/*
9923431Scarlsonj 	 * Special case: if we're "unplumbing" the main LIF for DHCPv4, then
9933431Scarlsonj 	 * just canonize it and remove it from the lease.
9943431Scarlsonj 	 */
9953431Scarlsonj 	if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) {
9965381Smeem 		canonize_lif(lif, B_TRUE);
9973431Scarlsonj 		cancel_lif_timers(lif);
9983431Scarlsonj 		if (lif->lif_declined != NULL) {
9993431Scarlsonj 			dlp->dl_smach->dsm_lif_down--;
10003431Scarlsonj 			lif->lif_declined = NULL;
10013431Scarlsonj 		}
10023431Scarlsonj 		dlp->dl_nlifs = 0;
10033431Scarlsonj 		dlp->dl_lifs = NULL;
10043431Scarlsonj 		lif->lif_lease = NULL;
10053431Scarlsonj 		release_lif(lif);
10063431Scarlsonj 	} else {
10073431Scarlsonj 		remove_lif(lif);
10083431Scarlsonj 	}
10093431Scarlsonj }
10103431Scarlsonj 
10113431Scarlsonj /*
10123431Scarlsonj  * attach_lif(): create a new logical interface, creating the physical
10133431Scarlsonj  *		 interface as necessary.
10143431Scarlsonj  *
10153431Scarlsonj  *   input: const char *: the logical interface name
10163431Scarlsonj  *	    boolean_t: B_TRUE for IPv6
10173431Scarlsonj  *	    int *: set to DHCP_IPC_E_* if creation fails
10183431Scarlsonj  *  output: dhcp_lif_t *: pointer to new entry, or NULL on failure
10193431Scarlsonj  */
10203431Scarlsonj 
10213431Scarlsonj dhcp_lif_t *
10223431Scarlsonj attach_lif(const char *lname, boolean_t isv6, int *error)
10233431Scarlsonj {
10243431Scarlsonj 	dhcp_pif_t *pif;
10253431Scarlsonj 	char pname[LIFNAMSIZ], *cp;
10263431Scarlsonj 
10273431Scarlsonj 	(void) strlcpy(pname, lname, sizeof (pname));
10283431Scarlsonj 	if ((cp = strchr(pname, ':')) != NULL)
10293431Scarlsonj 		*cp = '\0';
10303431Scarlsonj 
10313431Scarlsonj 	if ((pif = lookup_pif_by_name(pname, isv6)) != NULL)
10323431Scarlsonj 		hold_pif(pif);
10333431Scarlsonj 	else if ((pif = insert_pif(pname, isv6, error)) == NULL)
10343431Scarlsonj 		return (NULL);
10353431Scarlsonj 
10363431Scarlsonj 	if (lookup_lif_by_name(lname, pif) != NULL) {
10373431Scarlsonj 		dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!",
10383431Scarlsonj 		    lname);
10393431Scarlsonj 		release_pif(pif);
10403431Scarlsonj 		*error = DHCP_IPC_E_INVIF;
10413431Scarlsonj 		return (NULL);
10423431Scarlsonj 	}
10433431Scarlsonj 
10443431Scarlsonj 	/* If LIF creation fails, then insert_lif discards our PIF hold */
10453431Scarlsonj 	return (insert_lif(pif, lname, error));
10463431Scarlsonj }
10473431Scarlsonj 
10483431Scarlsonj /*
10493431Scarlsonj  * set_lif_dhcp(): Set logical interface flags to show that it's managed
10503431Scarlsonj  *		   by DHCP.
10513431Scarlsonj  *
10523431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
10533431Scarlsonj  *	    boolean_t: B_TRUE if adopting
10543431Scarlsonj  *  output: int: set to DHCP_IPC_E_* if operation fails
10553431Scarlsonj  */
10563431Scarlsonj 
10573431Scarlsonj int
10583431Scarlsonj set_lif_dhcp(dhcp_lif_t *lif, boolean_t is_adopting)
10593431Scarlsonj {
10603431Scarlsonj 	int fd;
10613431Scarlsonj 	int err;
10623431Scarlsonj 	struct lifreq lifr;
10633431Scarlsonj 
10643431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
10653431Scarlsonj 
10663431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
10673431Scarlsonj 
10683431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
10693431Scarlsonj 		err = errno;
10703431Scarlsonj 		dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s",
10713431Scarlsonj 		    lif->lif_name);
10723431Scarlsonj 		return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT);
10733431Scarlsonj 	}
10743431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
10753431Scarlsonj 
10763431Scarlsonj 	/*
10773431Scarlsonj 	 * Check for conflicting sources of address control, and other
10783431Scarlsonj 	 * unacceptable configurations.
10793431Scarlsonj 	 */
10804823Sseb 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
10814823Sseb 	    IFF_VIRTUAL)) {
10823431Scarlsonj 		dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx",
10833431Scarlsonj 		    lif->lif_name, lifr.lifr_flags);
10843431Scarlsonj 		return (DHCP_IPC_E_INVIF);
10853431Scarlsonj 	}
10863431Scarlsonj 
10870Sstevel@tonic-gate 	/*
10880Sstevel@tonic-gate 	 * if DHCPRUNNING is already set on the interface and we're
10890Sstevel@tonic-gate 	 * not adopting it, the agent probably crashed and burned.
10900Sstevel@tonic-gate 	 * note it, but don't let it stop the proceedings.  we're
10910Sstevel@tonic-gate 	 * pretty sure we're not already running, since we wouldn't
10920Sstevel@tonic-gate 	 * have been able to bind to our IPC port.
10930Sstevel@tonic-gate 	 */
10940Sstevel@tonic-gate 
10953431Scarlsonj 	if (lifr.lifr_flags & IFF_DHCPRUNNING) {
10963431Scarlsonj 		if (!is_adopting) {
10973431Scarlsonj 			dhcpmsg(MSG_WARNING, "set_lif_dhcp: DHCP flag already "
10983431Scarlsonj 			    "set on %s", lif->lif_name);
10993431Scarlsonj 		}
11003431Scarlsonj 	} else {
11013431Scarlsonj 		lifr.lifr_flags |= IFF_DHCPRUNNING;
11023431Scarlsonj 		if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
11033431Scarlsonj 			dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s",
11043431Scarlsonj 			    lif->lif_name);
11053431Scarlsonj 			return (DHCP_IPC_E_INT);
11063431Scarlsonj 		}
11073431Scarlsonj 		lif->lif_flags = lifr.lifr_flags;
11080Sstevel@tonic-gate 	}
11093431Scarlsonj 	return (DHCP_IPC_SUCCESS);
11103431Scarlsonj }
11110Sstevel@tonic-gate 
11123431Scarlsonj /*
11133431Scarlsonj  * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer
11143431Scarlsonj  *		     managed by DHCP.
11153431Scarlsonj  *
11163431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
11173431Scarlsonj  *  output: none
11183431Scarlsonj  */
11190Sstevel@tonic-gate 
11203431Scarlsonj static void
11213431Scarlsonj clear_lif_dhcp(dhcp_lif_t *lif)
11223431Scarlsonj {
11233431Scarlsonj 	int fd;
11243431Scarlsonj 	struct lifreq lifr;
11250Sstevel@tonic-gate 
11263431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
11273431Scarlsonj 
11283431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
11293431Scarlsonj 
11303431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
11313431Scarlsonj 		return;
11320Sstevel@tonic-gate 
11333431Scarlsonj 	if (!(lifr.lifr_flags & IFF_DHCPRUNNING))
11343431Scarlsonj 		return;
11350Sstevel@tonic-gate 
11363431Scarlsonj 	lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING;
11373431Scarlsonj 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
11383431Scarlsonj }
11390Sstevel@tonic-gate 
11403431Scarlsonj /*
11413431Scarlsonj  * set_lif_deprecated(): Set the "deprecated" flag to tell users that this
11423431Scarlsonj  *			 address will be going away.  As the interface is
11433431Scarlsonj  *			 going away, we don't care if there are errors.
11443431Scarlsonj  *
11453431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
11463431Scarlsonj  *  output: none
11473431Scarlsonj  */
11480Sstevel@tonic-gate 
11493431Scarlsonj void
11503431Scarlsonj set_lif_deprecated(dhcp_lif_t *lif)
11513431Scarlsonj {
11523431Scarlsonj 	int fd;
11533431Scarlsonj 	struct lifreq lifr;
11540Sstevel@tonic-gate 
11553431Scarlsonj 	if (lif->lif_flags & IFF_DEPRECATED)
11563431Scarlsonj 		return;
11570Sstevel@tonic-gate 
11583431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
11593431Scarlsonj 
11603431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
11610Sstevel@tonic-gate 
11623431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
11633431Scarlsonj 		return;
11643431Scarlsonj 
11653431Scarlsonj 	if (lifr.lifr_flags & IFF_DEPRECATED)
11663431Scarlsonj 		return;
11670Sstevel@tonic-gate 
11683431Scarlsonj 	lifr.lifr_flags |= IFF_DEPRECATED;
11693431Scarlsonj 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
11703431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
11713431Scarlsonj }
11723431Scarlsonj 
11733431Scarlsonj /*
11743431Scarlsonj  * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this
11753431Scarlsonj  *			   address will not be going away.  This happens if we
11763431Scarlsonj  *			   get a renewal after preferred lifetime but before
11773431Scarlsonj  *			   the valid lifetime.
11783431Scarlsonj  *
11793431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
11803431Scarlsonj  *  output: boolean_t: B_TRUE on success.
11813431Scarlsonj  */
11820Sstevel@tonic-gate 
11833431Scarlsonj boolean_t
11843431Scarlsonj clear_lif_deprecated(dhcp_lif_t *lif)
11853431Scarlsonj {
11863431Scarlsonj 	int fd;
11873431Scarlsonj 	struct lifreq lifr;
11883431Scarlsonj 
11893431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
11903431Scarlsonj 
11913431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
11923431Scarlsonj 
11933431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
11943431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s",
11953431Scarlsonj 		    lif->lif_name);
11963431Scarlsonj 		return (B_FALSE);
11970Sstevel@tonic-gate 	}
11980Sstevel@tonic-gate 
11990Sstevel@tonic-gate 	/*
12003431Scarlsonj 	 * Check for conflicting sources of address control, and other
12013431Scarlsonj 	 * unacceptable configurations.
12020Sstevel@tonic-gate 	 */
12034823Sseb 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
12044823Sseb 	    IFF_VIRTUAL)) {
12053431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags "
12063431Scarlsonj 		    "are %llx", lif->lif_name, lifr.lifr_flags);
12073431Scarlsonj 		return (B_FALSE);
12080Sstevel@tonic-gate 	}
12090Sstevel@tonic-gate 
12103431Scarlsonj 	if (!(lifr.lifr_flags & IFF_DEPRECATED))
12113431Scarlsonj 		return (B_TRUE);
12120Sstevel@tonic-gate 
12133431Scarlsonj 	lifr.lifr_flags &= ~IFF_DEPRECATED;
12143431Scarlsonj 	if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
12153431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s",
12163431Scarlsonj 		    lif->lif_name);
12173431Scarlsonj 		return (B_FALSE);
12183431Scarlsonj 	} else {
12193431Scarlsonj 		lif->lif_flags = lifr.lifr_flags;
12203431Scarlsonj 		return (B_TRUE);
12210Sstevel@tonic-gate 	}
12220Sstevel@tonic-gate }
12230Sstevel@tonic-gate 
12240Sstevel@tonic-gate /*
12253431Scarlsonj  * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only).
12260Sstevel@tonic-gate  *
12273431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
12285381Smeem  *	    in_addr_t: the address the socket will be bound to (in hbo)
12293431Scarlsonj  *  output: boolean_t: B_TRUE if the socket was opened successfully.
12300Sstevel@tonic-gate  */
12310Sstevel@tonic-gate 
12323431Scarlsonj boolean_t
12335381Smeem open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo)
12340Sstevel@tonic-gate {
12355381Smeem 	const char *errmsg;
12365381Smeem 	struct lifreq lifr;
12375381Smeem 	int on = 1;
12385455Smeem 	uchar_t ttl = 255;
12395381Smeem 
12403431Scarlsonj 	if (lif->lif_sock_ip_fd != -1) {
12413431Scarlsonj 		dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s",
12423431Scarlsonj 		    lif->lif_name);
12433431Scarlsonj 		return (B_FALSE);
12440Sstevel@tonic-gate 	}
12450Sstevel@tonic-gate 
12463431Scarlsonj 	lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
12473431Scarlsonj 	if (lif->lif_sock_ip_fd == -1) {
12485381Smeem 		errmsg = "cannot create v4 socket";
12495381Smeem 		goto failure;
12505381Smeem 	}
12515381Smeem 
12525381Smeem 	if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, addr_hbo)) {
12535381Smeem 		errmsg = "cannot bind v4 socket";
12545381Smeem 		goto failure;
12555381Smeem 	}
12565381Smeem 
12575381Smeem 	/*
12585381Smeem 	 * If we bound to INADDR_ANY, we have no IFF_UP source address to use.
12595381Smeem 	 * Thus, enable IP_UNSPEC_SRC so that we can send packets with an
12605381Smeem 	 * unspecified (0.0.0.0) address.  Also, enable IP_DHCPINIT_IF so that
12615381Smeem 	 * the IP module will accept unicast DHCP traffic regardless of the IP
12625381Smeem 	 * address it's sent to.  (We'll then figure out which packets are
12635381Smeem 	 * ours based on the xid.)
12645381Smeem 	 */
12655381Smeem 	if (addr_hbo == INADDR_ANY) {
12665381Smeem 		if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_UNSPEC_SRC,
12675381Smeem 		    &on, sizeof (int)) == -1) {
12685381Smeem 			errmsg = "cannot set IP_UNSPEC_SRC";
12695381Smeem 			goto failure;
12705381Smeem 		}
12715381Smeem 
12725381Smeem 		if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_DHCPINIT_IF,
12735381Smeem 		    &lif->lif_pif->pif_index, sizeof (int)) == -1) {
12745381Smeem 			errmsg = "cannot set IP_DHCPINIT_IF";
12755381Smeem 			goto failure;
12765381Smeem 		}
12775381Smeem 	}
12785381Smeem 
12795455Smeem 	/*
12805455Smeem 	 * Unfortunately, some hardware (such as the Linksys WRT54GC)
12815455Smeem 	 * decrements the TTL *prior* to accepting DHCP traffic destined
12825455Smeem 	 * for it.  To workaround this, tell IP to use a TTL of 255 for
12835455Smeem 	 * broadcast packets sent from this socket.
12845455Smeem 	 */
12855455Smeem 	if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BROADCAST_TTL, &ttl,
12865455Smeem 	    sizeof (uchar_t)) == -1) {
12875455Smeem 		errmsg = "cannot set IP_BROADCAST_TTL";
12885455Smeem 		goto failure;
12895455Smeem 	}
12905455Smeem 
12915381Smeem 	if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF,
12925381Smeem 	    &lif->lif_pif->pif_index, sizeof (int)) == -1) {
12935381Smeem 		errmsg = "cannot set IP_BOUND_IF";
12945381Smeem 		goto failure;
12950Sstevel@tonic-gate 	}
12960Sstevel@tonic-gate 
12975381Smeem 	/*
12985381Smeem 	 * Make sure at least one lif on the interface we used in IP_BOUND_IF
12995381Smeem 	 * is IFF_UP so that we can send and receive IP packets.
13005381Smeem 	 */
13015381Smeem 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
13025381Smeem 	if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
13035381Smeem 		errmsg = "cannot get interface flags";
13045381Smeem 		goto failure;
13053431Scarlsonj 	}
13060Sstevel@tonic-gate 
13075381Smeem 	if (!(lifr.lifr_flags & IFF_UP)) {
13085381Smeem 		/*
13095381Smeem 		 * Start from a clean slate.
13105381Smeem 		 */
13115381Smeem 		canonize_lif(lif, B_FALSE);
13125381Smeem 
13135381Smeem 		lifr.lifr_flags |= IFF_UP;
13145381Smeem 		if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
13155381Smeem 			errmsg = "cannot bring up";
13165381Smeem 			goto failure;
13175381Smeem 		}
13185439Smeem 		lif->lif_flags = lifr.lifr_flags;
13195381Smeem 
13205381Smeem 		/*
13215381Smeem 		 * When bringing 0.0.0.0 IFF_UP, the kernel changes the
13225381Smeem 		 * netmask to 255.0.0.0, so re-fetch our expected netmask.
13235381Smeem 		 */
13245381Smeem 		if (ioctl(v4_sock_fd, SIOCGLIFNETMASK, &lifr) == -1) {
13255381Smeem 			errmsg = "cannot get netmask";
13265381Smeem 			goto failure;
13275381Smeem 		}
13285381Smeem 
13295381Smeem 		lif->lif_netmask =
13305381Smeem 		    ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr;
13310Sstevel@tonic-gate 	}
13325381Smeem 
13335381Smeem 	lif->lif_packet_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN,
13345381Smeem 	    dhcp_packet_lif, lif);
13355381Smeem 	if (lif->lif_packet_id == -1) {
13365381Smeem 		errmsg = "cannot register to receive DHCP packets";
13375381Smeem 		goto failure;
13385381Smeem 	}
13395381Smeem 
13400Sstevel@tonic-gate 	return (B_TRUE);
13415381Smeem failure:
13425381Smeem 	dhcpmsg(MSG_ERR, "open_ip_lif: %s: %s", lif->lif_name, errmsg);
13435381Smeem 	close_ip_lif(lif);
13445381Smeem 	return (B_FALSE);
13450Sstevel@tonic-gate }
13460Sstevel@tonic-gate 
13470Sstevel@tonic-gate /*
13483431Scarlsonj  * close_ip_lif(): close an IP socket for I/O on a given LIF.
13490Sstevel@tonic-gate  *
13503431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
13513431Scarlsonj  *  output: none
13520Sstevel@tonic-gate  */
13530Sstevel@tonic-gate 
13543431Scarlsonj void
13553431Scarlsonj close_ip_lif(dhcp_lif_t *lif)
13560Sstevel@tonic-gate {
13575381Smeem 	if (lif->lif_packet_id != -1) {
13585381Smeem 		(void) iu_unregister_event(eh, lif->lif_packet_id, NULL);
13595381Smeem 		lif->lif_packet_id = -1;
13603431Scarlsonj 	}
13613431Scarlsonj 	if (lif->lif_sock_ip_fd != -1) {
13623431Scarlsonj 		(void) close(lif->lif_sock_ip_fd);
13633431Scarlsonj 		lif->lif_sock_ip_fd = -1;
13643431Scarlsonj 	}
13653431Scarlsonj }
13660Sstevel@tonic-gate 
13673431Scarlsonj /*
13683431Scarlsonj  * lif_mark_decline(): mark a LIF as having been declined due to a duplicate
13693431Scarlsonj  *		       address or some other conflict.  This is used in
13703431Scarlsonj  *		       send_declines() to report failure back to the server.
13713431Scarlsonj  *
13723431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
13733431Scarlsonj  *	    const char *: text string explaining why the address is declined
13743431Scarlsonj  *  output: none
13753431Scarlsonj  */
13760Sstevel@tonic-gate 
13773431Scarlsonj void
13783431Scarlsonj lif_mark_decline(dhcp_lif_t *lif, const char *reason)
13793431Scarlsonj {
13803431Scarlsonj 	if (lif->lif_declined == NULL) {
13813431Scarlsonj 		dhcp_lease_t *dlp;
13820Sstevel@tonic-gate 
13833431Scarlsonj 		lif->lif_declined = reason;
13843431Scarlsonj 		if ((dlp = lif->lif_lease) != NULL)
13853431Scarlsonj 			dlp->dl_smach->dsm_lif_down++;
13860Sstevel@tonic-gate 	}
13870Sstevel@tonic-gate }
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate /*
13903431Scarlsonj  * schedule_lif_timer(): schedules the LIF-related timer
13910Sstevel@tonic-gate  *
13923431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
13933431Scarlsonj  *	    dhcp_timer_t *: the timer to schedule
13943431Scarlsonj  *	    iu_tq_callback_t *: the callback to call upon firing
13953431Scarlsonj  *  output: boolean_t: B_TRUE if the timer was scheduled successfully
13960Sstevel@tonic-gate  */
13970Sstevel@tonic-gate 
13983431Scarlsonj boolean_t
13993431Scarlsonj schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire)
14000Sstevel@tonic-gate {
14010Sstevel@tonic-gate 	/*
14023431Scarlsonj 	 * If there's a timer running, cancel it and release its lease
14033431Scarlsonj 	 * reference.
14040Sstevel@tonic-gate 	 */
14053431Scarlsonj 	if (dt->dt_id != -1) {
14063431Scarlsonj 		if (!cancel_timer(dt))
14073431Scarlsonj 			return (B_FALSE);
14083431Scarlsonj 		release_lif(lif);
14093431Scarlsonj 	}
14100Sstevel@tonic-gate 
14113431Scarlsonj 	if (schedule_timer(dt, expire, lif)) {
14123431Scarlsonj 		hold_lif(lif);
14133431Scarlsonj 		return (B_TRUE);
14143431Scarlsonj 	} else {
14153431Scarlsonj 		dhcpmsg(MSG_WARNING,
14163431Scarlsonj 		    "schedule_lif_timer: cannot schedule timer");
14173431Scarlsonj 		return (B_FALSE);
14183431Scarlsonj 	}
14190Sstevel@tonic-gate }
14200Sstevel@tonic-gate 
14210Sstevel@tonic-gate /*
14223431Scarlsonj  * cancel_lif_timer(): cancels a LIF-related timer
14230Sstevel@tonic-gate  *
14243431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
14253431Scarlsonj  *	    dhcp_timer_t *: the timer to cancel
14263431Scarlsonj  *  output: none
14270Sstevel@tonic-gate  */
14280Sstevel@tonic-gate 
14293431Scarlsonj static void
14303431Scarlsonj cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt)
14310Sstevel@tonic-gate {
14323431Scarlsonj 	if (dt->dt_id == -1)
14333431Scarlsonj 		return;
14343431Scarlsonj 	if (cancel_timer(dt)) {
14353431Scarlsonj 		dhcpmsg(MSG_DEBUG2,
14363431Scarlsonj 		    "cancel_lif_timer: canceled expiry timer on %s",
14373431Scarlsonj 		    lif->lif_name);
14383431Scarlsonj 		release_lif(lif);
14393431Scarlsonj 	} else {
14403431Scarlsonj 		dhcpmsg(MSG_WARNING,
14413431Scarlsonj 		    "cancel_lif_timer: cannot cancel timer on %s",
14423431Scarlsonj 		    lif->lif_name);
14430Sstevel@tonic-gate 	}
14440Sstevel@tonic-gate }
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate /*
14473431Scarlsonj  * cancel_lif_timers(): cancels the LIF-related timers
14480Sstevel@tonic-gate  *
14493431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
14503431Scarlsonj  *  output: none
14510Sstevel@tonic-gate  */
14520Sstevel@tonic-gate 
14530Sstevel@tonic-gate void
14543431Scarlsonj cancel_lif_timers(dhcp_lif_t *lif)
14550Sstevel@tonic-gate {
14563431Scarlsonj 	cancel_lif_timer(lif, &lif->lif_preferred);
14573431Scarlsonj 	cancel_lif_timer(lif, &lif->lif_expire);
14580Sstevel@tonic-gate }
14590Sstevel@tonic-gate 
14600Sstevel@tonic-gate /*
14613431Scarlsonj  * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common
14623431Scarlsonj  *		  file descriptors (v4_sock_fd and v6_sock_fd).
14630Sstevel@tonic-gate  *
14643431Scarlsonj  *   input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4
14653431Scarlsonj  *  output: none
14660Sstevel@tonic-gate  */
14670Sstevel@tonic-gate 
14683431Scarlsonj uint_t
14693431Scarlsonj get_max_mtu(boolean_t isv6)
14700Sstevel@tonic-gate {
14713431Scarlsonj 	uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu;
14723431Scarlsonj 
14733431Scarlsonj 	if (*mtup == 0) {
14743431Scarlsonj 		dhcp_pif_t *pif;
14753431Scarlsonj 		dhcp_lif_t *lif;
14763431Scarlsonj 		struct lifreq lifr;
14773431Scarlsonj 
14783431Scarlsonj 		/* Set an arbitrary lower bound */
14793431Scarlsonj 		*mtup = 1024;
14803431Scarlsonj 		pif = isv6 ? v6root : v4root;
14813431Scarlsonj 		for (; pif != NULL; pif = pif->pif_next) {
14823431Scarlsonj 			for (lif = pif->pif_lifs; lif != NULL;
14833431Scarlsonj 			    lif = lif->lif_next) {
14843431Scarlsonj 				(void) strlcpy(lifr.lifr_name, lif->lif_name,
14853431Scarlsonj 				    LIFNAMSIZ);
14863431Scarlsonj 				if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) !=
14873431Scarlsonj 				    -1 && lifr.lifr_mtu > *mtup) {
14883431Scarlsonj 					*mtup = lifr.lifr_mtu;
14893431Scarlsonj 				}
14903431Scarlsonj 			}
14913431Scarlsonj 		}
14920Sstevel@tonic-gate 	}
14933431Scarlsonj 	return (*mtup);
14940Sstevel@tonic-gate }
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate /*
14973431Scarlsonj  * expired_lif_state(): summarize the state of expired LIFs on a given state
14983431Scarlsonj  *			machine.
14990Sstevel@tonic-gate  *
15003431Scarlsonj  *   input: dhcp_smach_t *: the state machine to scan
15013431Scarlsonj  *  output: dhcp_expire_t: overall state
15020Sstevel@tonic-gate  */
15030Sstevel@tonic-gate 
15043431Scarlsonj dhcp_expire_t
15053431Scarlsonj expired_lif_state(dhcp_smach_t *dsmp)
15060Sstevel@tonic-gate {
15073431Scarlsonj 	dhcp_lease_t *dlp;
15083431Scarlsonj 	dhcp_lif_t *lif;
15093431Scarlsonj 	uint_t nlifs;
15103431Scarlsonj 	uint_t numlifs;
15113431Scarlsonj 	uint_t numexp;
15120Sstevel@tonic-gate 
15133431Scarlsonj 	numlifs = numexp = 0;
15143431Scarlsonj 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
15153431Scarlsonj 		lif = dlp->dl_lifs;
15163431Scarlsonj 		nlifs = dlp->dl_nlifs;
15173431Scarlsonj 		numlifs += nlifs;
15183431Scarlsonj 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
15193431Scarlsonj 			if (lif->lif_expired)
15203431Scarlsonj 				numexp++;
15213431Scarlsonj 		}
15220Sstevel@tonic-gate 	}
15233431Scarlsonj 	if (numlifs == 0)
15243431Scarlsonj 		return (DHCP_EXP_NOLIFS);
15253431Scarlsonj 	else if (numexp == 0)
15263431Scarlsonj 		return (DHCP_EXP_NOEXP);
15273431Scarlsonj 	else if (numlifs == numexp)
15283431Scarlsonj 		return (DHCP_EXP_ALLEXP);
15293431Scarlsonj 	else
15303431Scarlsonj 		return (DHCP_EXP_SOMEEXP);
15310Sstevel@tonic-gate }
15320Sstevel@tonic-gate 
15330Sstevel@tonic-gate /*
15343431Scarlsonj  * find_expired_lif(): find the first expired LIF on a given state machine
15350Sstevel@tonic-gate  *
15363431Scarlsonj  *   input: dhcp_smach_t *: the state machine to scan
15373431Scarlsonj  *  output: dhcp_lif_t *: the first expired LIF, or NULL if none.
15380Sstevel@tonic-gate  */
15390Sstevel@tonic-gate 
15403431Scarlsonj dhcp_lif_t *
15413431Scarlsonj find_expired_lif(dhcp_smach_t *dsmp)
15420Sstevel@tonic-gate {
15433431Scarlsonj 	dhcp_lease_t *dlp;
15443431Scarlsonj 	dhcp_lif_t *lif;
15453431Scarlsonj 	uint_t nlifs;
15463431Scarlsonj 
15473431Scarlsonj 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
15483431Scarlsonj 		lif = dlp->dl_lifs;
15493431Scarlsonj 		nlifs = dlp->dl_nlifs;
15503431Scarlsonj 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
15513431Scarlsonj 			if (lif->lif_expired)
15523431Scarlsonj 				return (lif);
15533431Scarlsonj 		}
15543431Scarlsonj 	}
15553431Scarlsonj 	return (NULL);
15563431Scarlsonj }
15573431Scarlsonj 
15583431Scarlsonj /*
15593431Scarlsonj  * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING.  Used
15603431Scarlsonj  *		       only for DHCPv6.
15613431Scarlsonj  *
15623431Scarlsonj  *   input: none
15633431Scarlsonj  *  output: none
15643431Scarlsonj  */
15653431Scarlsonj 
15663431Scarlsonj void
15673431Scarlsonj remove_v6_strays(void)
15683431Scarlsonj {
15693431Scarlsonj 	struct lifnum lifn;
15703431Scarlsonj 	struct lifconf lifc;
15713431Scarlsonj 	struct lifreq *lifrp, *lifrmax;
15723431Scarlsonj 	uint_t numifs;
15733431Scarlsonj 	uint64_t flags;
15740Sstevel@tonic-gate 
15750Sstevel@tonic-gate 	/*
15763431Scarlsonj 	 * Get the approximate number of interfaces in the system.  It's only
15773431Scarlsonj 	 * approximate because the system is dynamic -- interfaces may be
15783431Scarlsonj 	 * plumbed or unplumbed at any time.  This is also the reason for the
15793431Scarlsonj 	 * "+ 10" fudge factor: we're trying to avoid unnecessary looping.
15800Sstevel@tonic-gate 	 */
15813431Scarlsonj 	(void) memset(&lifn, 0, sizeof (lifn));
15823431Scarlsonj 	lifn.lifn_family = AF_INET6;
15833431Scarlsonj 	lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
15843431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) {
15853431Scarlsonj 		dhcpmsg(MSG_ERR,
15863431Scarlsonj 		    "remove_v6_strays: cannot read number of interfaces");
15873431Scarlsonj 		numifs = 10;
15883431Scarlsonj 	} else {
15893431Scarlsonj 		numifs = lifn.lifn_count + 10;
15900Sstevel@tonic-gate 	}
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate 	/*
15933431Scarlsonj 	 * Get the interface information.  We do this in a loop so that we can
15943431Scarlsonj 	 * recover from EINVAL from the kernel -- delivered when the buffer is
15953431Scarlsonj 	 * too small.
15960Sstevel@tonic-gate 	 */
15973431Scarlsonj 	(void) memset(&lifc, 0, sizeof (lifc));
15983431Scarlsonj 	lifc.lifc_family = AF_INET6;
15993431Scarlsonj 	lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
16003431Scarlsonj 	for (;;) {
16013431Scarlsonj 		lifc.lifc_len = numifs * sizeof (*lifrp);
16023431Scarlsonj 		lifrp = realloc(lifc.lifc_buf, lifc.lifc_len);
16033431Scarlsonj 		if (lifrp == NULL) {
16043431Scarlsonj 			dhcpmsg(MSG_ERR,
16053431Scarlsonj 			    "remove_v6_strays: cannot allocate memory");
16063431Scarlsonj 			free(lifc.lifc_buf);
16073431Scarlsonj 			return;
16080Sstevel@tonic-gate 		}
16093431Scarlsonj 		lifc.lifc_buf = (caddr_t)lifrp;
16103431Scarlsonj 		errno = 0;
16113431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 &&
16123431Scarlsonj 		    lifc.lifc_len < numifs * sizeof (*lifrp))
16130Sstevel@tonic-gate 			break;
16143431Scarlsonj 		if (errno == 0 || errno == EINVAL) {
16153431Scarlsonj 			numifs <<= 1;
16163431Scarlsonj 		} else {
16173431Scarlsonj 			dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF");
16183431Scarlsonj 			free(lifc.lifc_buf);
16193431Scarlsonj 			return;
16200Sstevel@tonic-gate 		}
16210Sstevel@tonic-gate 	}
16220Sstevel@tonic-gate 
16233431Scarlsonj 	lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp);
16243431Scarlsonj 	for (; lifrp < lifrmax; lifrp++) {
16250Sstevel@tonic-gate 		/*
16263431Scarlsonj 		 * Get the interface flags; we're interested in the DHCP ones.
16270Sstevel@tonic-gate 		 */
16283431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1)
16293431Scarlsonj 			continue;
16303431Scarlsonj 		flags = lifrp->lifr_flags;
16313431Scarlsonj 		if (!(flags & IFF_DHCPRUNNING))
16323431Scarlsonj 			continue;
16330Sstevel@tonic-gate 		/*
16343431Scarlsonj 		 * If the interface has a link-local address, then we don't
16353431Scarlsonj 		 * control it.  Just remove the flag.
16360Sstevel@tonic-gate 		 */
16373431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1)
16383431Scarlsonj 			continue;
16393431Scarlsonj 		if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp->
16403431Scarlsonj 		    lifr_addr)->sin6_addr)) {
16413431Scarlsonj 			lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING;
16423431Scarlsonj 			(void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp);
16433431Scarlsonj 			continue;
16440Sstevel@tonic-gate 		}
16450Sstevel@tonic-gate 		/*
16463431Scarlsonj 		 * All others are (or were) under our control.  Clean up by
16473431Scarlsonj 		 * removing them.
16480Sstevel@tonic-gate 		 */
16493431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) {
16503431Scarlsonj 			dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s",
16513431Scarlsonj 			    lifrp->lifr_name);
16523431Scarlsonj 		} else if (errno != ENXIO) {
16533431Scarlsonj 			dhcpmsg(MSG_ERR,
16543431Scarlsonj 			    "remove_v6_strays: SIOCLIFREMOVEIF %s",
16553431Scarlsonj 			    lifrp->lifr_name);
16560Sstevel@tonic-gate 		}
16570Sstevel@tonic-gate 	}
16583431Scarlsonj 	free(lifc.lifc_buf);
16590Sstevel@tonic-gate }
1660