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 /*
223431Scarlsonj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <sys/types.h>
290Sstevel@tonic-gate #include <sys/socket.h>
300Sstevel@tonic-gate #include <net/if.h>
310Sstevel@tonic-gate #include <stdlib.h>
320Sstevel@tonic-gate #include <sys/sockio.h>
330Sstevel@tonic-gate #include <netinet/in.h>
340Sstevel@tonic-gate #include <netinet/dhcp.h>
350Sstevel@tonic-gate #include <string.h>
360Sstevel@tonic-gate #include <unistd.h>
373431Scarlsonj #include <search.h>
383431Scarlsonj #include <libdevinfo.h>
394456Sss150715 #include <libdlpi.h>
400Sstevel@tonic-gate #include <netinet/if_ether.h>
413431Scarlsonj #include <arpa/inet.h>
420Sstevel@tonic-gate #include <dhcpmsg.h>
433431Scarlsonj #include <dhcp_inittab.h>
444456Sss150715 #include <stropts.h>
450Sstevel@tonic-gate 
463431Scarlsonj #include "agent.h"
470Sstevel@tonic-gate #include "interface.h"
480Sstevel@tonic-gate #include "util.h"
490Sstevel@tonic-gate #include "dlpi_io.h"
500Sstevel@tonic-gate #include "packet.h"
510Sstevel@tonic-gate #include "states.h"
523431Scarlsonj 
533431Scarlsonj dhcp_pif_t *v4root;
543431Scarlsonj dhcp_pif_t *v6root;
553431Scarlsonj 
563431Scarlsonj static uint_t cached_v4_max_mtu, cached_v6_max_mtu;
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /*
593431Scarlsonj  * Interface flags to watch: things that should be under our direct control.
600Sstevel@tonic-gate  */
61*4823Sseb #define	DHCP_IFF_WATCH	(IFF_DHCPRUNNING | IFF_DEPRECATED | IFF_ADDRCONF | \
62*4823Sseb 	IFF_TEMPORARY)
630Sstevel@tonic-gate 
643431Scarlsonj static void clear_lif_dhcp(dhcp_lif_t *);
650Sstevel@tonic-gate 
660Sstevel@tonic-gate /*
673431Scarlsonj  * insert_pif(): creates a new physical interface structure and chains it on
683431Scarlsonj  *		 the list.  Initializes state that remains consistent across
693431Scarlsonj  *		 all use of the physical interface entry.
700Sstevel@tonic-gate  *
713431Scarlsonj  *   input: const char *: the name of the physical interface
723431Scarlsonj  *	    boolean_t: if B_TRUE, this is DHCPv6
733431Scarlsonj  *	    int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
740Sstevel@tonic-gate  *		   error code with the reason why
753431Scarlsonj  *  output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure
760Sstevel@tonic-gate  */
770Sstevel@tonic-gate 
783431Scarlsonj dhcp_pif_t *
793431Scarlsonj insert_pif(const char *pname, boolean_t isv6, int *error)
800Sstevel@tonic-gate {
813431Scarlsonj 	dhcp_pif_t *pif;
823431Scarlsonj 	struct lifreq lifr;
830Sstevel@tonic-gate 
843431Scarlsonj 	if ((pif = calloc(1, sizeof (*pif))) == NULL) {
853431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for "
863431Scarlsonj 		    "%s", pname);
873431Scarlsonj 		*error = DHCP_IPC_E_MEMORY;
880Sstevel@tonic-gate 		return (NULL);
890Sstevel@tonic-gate 	}
900Sstevel@tonic-gate 
913431Scarlsonj 	pif->pif_isv6 = isv6;
924456Sss150715 	pif->pif_dlpi_hd = NULL;
933431Scarlsonj 	pif->pif_dlpi_id = -1;
943431Scarlsonj 	pif->pif_hold_count = 1;
953431Scarlsonj 	pif->pif_running = B_TRUE;
963431Scarlsonj 
973431Scarlsonj 	if (strlcpy(pif->pif_name, pname, LIFNAMSIZ) >= LIFNAMSIZ) {
983431Scarlsonj 		dhcpmsg(MSG_ERROR, "insert_pif: interface name %s is too long",
993431Scarlsonj 		    pname);
1003431Scarlsonj 		*error = DHCP_IPC_E_INVIF;
1013431Scarlsonj 		goto failure;
1023431Scarlsonj 	}
1033431Scarlsonj 
1043431Scarlsonj 	/* We do not use DLPI with DHCPv6 */
1053431Scarlsonj 	if (!isv6) {
1064456Sss150715 		int			rc;
1074456Sss150715 		dlpi_handle_t		dh;
1084456Sss150715 		dlpi_info_t		dlinfo;
1093431Scarlsonj 
1103431Scarlsonj 		/*
1113431Scarlsonj 		 * Do the allocations necessary for IPv4 DHCP.
1123431Scarlsonj 		 *
1133431Scarlsonj 		 *  1. open the interface using DLPI
1143431Scarlsonj 		 *  2. get the interface max SDU
1153431Scarlsonj 		 *  3. get the interface hardware type and hardware length
1163431Scarlsonj 		 *  4. get the interface hardware address
1173431Scarlsonj 		 *  5. get the interface hardware broadcast address
1183431Scarlsonj 		 */
1193431Scarlsonj 
1203431Scarlsonj 		/* step 1 */
1214456Sss150715 		if ((rc = dlpi_open(pname, &dh, 0)) != DLPI_SUCCESS) {
1224456Sss150715 			dhcpmsg(MSG_ERROR, "insert_pif: dlpi_open: %s",
1234456Sss150715 			    dlpi_strerror(rc));
1244456Sss150715 			*error = DHCP_IPC_E_INVIF;
1254456Sss150715 			goto failure;
1264456Sss150715 		}
1274456Sss150715 		pif->pif_dlpi_hd = dh;
1284456Sss150715 
1294456Sss150715 		if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) {
1304456Sss150715 			dhcpmsg(MSG_ERROR, "insert_pif: dlpi_bind: %s",
1314456Sss150715 			    dlpi_strerror(rc));
1323431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
1333431Scarlsonj 			goto failure;
1343431Scarlsonj 		}
1353431Scarlsonj 
1363431Scarlsonj 		/* step 2 */
1374456Sss150715 		rc = dlpi_info(pif->pif_dlpi_hd, &dlinfo, 0);
1384456Sss150715 		if (rc != DLPI_SUCCESS) {
1394456Sss150715 			dhcpmsg(MSG_ERROR, "insert_pif: dlpi_info: %s",
1404456Sss150715 			    dlpi_strerror(rc));
1414456Sss150715 			*error = DHCP_IPC_E_INVIF;
1424456Sss150715 			goto failure;
1434456Sss150715 		}
1444456Sss150715 
1454456Sss150715 		pif->pif_max = dlinfo.di_max_sdu;
1463431Scarlsonj 		if (pif->pif_max < DHCP_DEF_MAX_SIZE) {
1473431Scarlsonj 			dhcpmsg(MSG_ERROR, "insert_pif: %s does not have a "
1483431Scarlsonj 			    "large enough maximum SDU to support DHCP "
1493431Scarlsonj 			    "(%u < %u)", pname, pif->pif_max,
1503431Scarlsonj 			    DHCP_DEF_MAX_SIZE);
1513431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
1523431Scarlsonj 			goto failure;
1533431Scarlsonj 		}
1543431Scarlsonj 
1553431Scarlsonj 		/* step 3 */
1564456Sss150715 		pif->pif_hwtype = dlpi_arptype(dlinfo.di_mactype);
1574456Sss150715 		pif->pif_hwlen  = dlinfo.di_physaddrlen;
1583431Scarlsonj 
1593431Scarlsonj 		dhcpmsg(MSG_DEBUG, "insert_pif: %s: sdumax %u, hwtype %d, "
1603431Scarlsonj 		    "hwlen %d", pname, pif->pif_max, pif->pif_hwtype,
1613431Scarlsonj 		    pif->pif_hwlen);
1623431Scarlsonj 
1633431Scarlsonj 		/* step 4 */
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 			}
1723431Scarlsonj 		}
1733431Scarlsonj 
1744456Sss150715 		(void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr,
1754456Sss150715 		    pif->pif_hwlen);
1764456Sss150715 
1773431Scarlsonj 		/*
1784456Sss150715 		 * step 5
1794456Sss150715 		 * Some media types has no broadcast address.
1803431Scarlsonj 		 */
1814456Sss150715 		if ((pif->pif_dlen = dlinfo.di_bcastaddrlen) != 0) {
1824456Sss150715 			pif->pif_daddr = malloc(pif->pif_dlen);
1834456Sss150715 			if (pif->pif_daddr == NULL) {
1844456Sss150715 				dhcpmsg(MSG_ERR, "insert_pif: cannot allocate "
1854456Sss150715 				    "pif_daddr for %s", pname);
1864456Sss150715 				*error = DHCP_IPC_E_MEMORY;
1874456Sss150715 				goto failure;
1884456Sss150715 			}
1893431Scarlsonj 		}
1904456Sss150715 		(void) memcpy(pif->pif_daddr, dlinfo.di_bcastaddr,
1914456Sss150715 		    pif->pif_dlen);
1923431Scarlsonj 
1933431Scarlsonj 		/* Close the DLPI stream until actually needed */
1943431Scarlsonj 		close_dlpi_pif(pif);
1953431Scarlsonj 	}
1963431Scarlsonj 
1970Sstevel@tonic-gate 	/*
1983431Scarlsonj 	 * This is a bit gross, but IP has a confused interface.  We must
1993431Scarlsonj 	 * assume that the zeroth LIF is plumbed, and must query there to get
2003431Scarlsonj 	 * the interface index number.
2010Sstevel@tonic-gate 	 */
2023431Scarlsonj 	(void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ);
2030Sstevel@tonic-gate 
2043431Scarlsonj 	if (ioctl(isv6 ? v6_sock_fd : v4_sock_fd, SIOCGLIFINDEX, &lifr) == -1) {
2053431Scarlsonj 		if (errno == ENXIO)
2063431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
2073431Scarlsonj 		else
2083431Scarlsonj 			*error = DHCP_IPC_E_INT;
2093431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname);
2103431Scarlsonj 		goto failure;
2113431Scarlsonj 	}
2123431Scarlsonj 	pif->pif_index = lifr.lifr_index;
2133431Scarlsonj 
2143431Scarlsonj 	insque(pif, isv6 ? &v6root : &v4root);
2153431Scarlsonj 
2163431Scarlsonj 	return (pif);
2173431Scarlsonj 
2183431Scarlsonj failure:
2193431Scarlsonj 	release_pif(pif);
2203431Scarlsonj 	return (NULL);
2213431Scarlsonj }
2223431Scarlsonj 
2233431Scarlsonj /*
2243431Scarlsonj  * hold_pif(): acquire a hold on a physical interface structure.
2253431Scarlsonj  *
2263431Scarlsonj  *   input: dhcp_pif_t *: a pointer to the PIF structure
2273431Scarlsonj  *  output: none
2283431Scarlsonj  */
2293431Scarlsonj 
2303431Scarlsonj void
2313431Scarlsonj hold_pif(dhcp_pif_t *pif)
2323431Scarlsonj {
2333431Scarlsonj 	pif->pif_hold_count++;
2343431Scarlsonj 	dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name,
2353431Scarlsonj 	    pif->pif_hold_count);
2363431Scarlsonj }
2373431Scarlsonj 
2383431Scarlsonj /*
2393431Scarlsonj  * release_pif(): release a hold on a physical interface structure; will
2403431Scarlsonj  *		  destroy the structure on the last hold removed.
2413431Scarlsonj  *
2423431Scarlsonj  *   input: dhcp_pif_t *: a pointer to the PIF structure
2433431Scarlsonj  *  output: none
2443431Scarlsonj  */
2453431Scarlsonj 
2463431Scarlsonj void
2473431Scarlsonj release_pif(dhcp_pif_t *pif)
2483431Scarlsonj {
2493431Scarlsonj 	if (pif->pif_hold_count == 0) {
2503431Scarlsonj 		dhcpmsg(MSG_CRIT, "release_pif: extraneous release");
2513431Scarlsonj 		return;
2523431Scarlsonj 	}
2533431Scarlsonj 
2543431Scarlsonj 	if (--pif->pif_hold_count == 0) {
2553431Scarlsonj 		dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s",
2563431Scarlsonj 		    pif->pif_name);
2573431Scarlsonj 
2583431Scarlsonj 		remque(pif);
2593431Scarlsonj 		pif->pif_dlpi_count = 1;
2603431Scarlsonj 		close_dlpi_pif(pif);
2613431Scarlsonj 		free(pif->pif_hwaddr);
2623431Scarlsonj 		free(pif->pif_daddr);
2633431Scarlsonj 		free(pif);
2643431Scarlsonj 	} else {
2653431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u",
2663431Scarlsonj 		    pif->pif_name, pif->pif_hold_count);
2673431Scarlsonj 	}
2683431Scarlsonj }
2693431Scarlsonj 
2703431Scarlsonj /*
2713431Scarlsonj  * lookup_pif_by_index(): Looks up PIF entries given regular ifIndex.
2723431Scarlsonj  *
2733431Scarlsonj  *   input: uint_t: the interface index
2743431Scarlsonj  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
2753431Scarlsonj  *  output: dhcp_pif_t *: the matching PIF, or NULL if not found
2763431Scarlsonj  */
2773431Scarlsonj 
2783431Scarlsonj dhcp_pif_t *
2793431Scarlsonj lookup_pif_by_index(uint_t ifindex, boolean_t isv6)
2803431Scarlsonj {
2813431Scarlsonj 	dhcp_pif_t *pif;
2823431Scarlsonj 
2833431Scarlsonj 	for (pif = isv6 ? v6root : v4root; pif != NULL; pif = pif->pif_next) {
2843431Scarlsonj 		if (pif->pif_index == ifindex)
2853431Scarlsonj 			break;
2863431Scarlsonj 	}
2873431Scarlsonj 
2883431Scarlsonj 	return (pif);
2893431Scarlsonj }
2903431Scarlsonj 
2913431Scarlsonj /*
2923431Scarlsonj  * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and
2933431Scarlsonj  *			   previous PIF pointer (or NULL for list start).
2943431Scarlsonj  *			   Caller is expected to iterate through all
2953431Scarlsonj  *			   potential matches to find interface of interest.
2963431Scarlsonj  *
2973431Scarlsonj  *   input: uint16_t: the interface index (truncated)
2983431Scarlsonj  *	    dhcp_pif_t *: the previous PIF, or NULL for list start
2993431Scarlsonj  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
3003431Scarlsonj  *  output: dhcp_pif_t *: the next matching PIF, or NULL if not found
3013431Scarlsonj  *    note: This operates using the 'truncated' (16-bit) ifindex as seen by
3023431Scarlsonj  *	    routing socket clients.  The value stored in pif_index is the
3033431Scarlsonj  *	    32-bit ifindex from the ioctl interface.
3043431Scarlsonj  */
3053431Scarlsonj 
3063431Scarlsonj dhcp_pif_t *
3073431Scarlsonj lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6)
3083431Scarlsonj {
3093431Scarlsonj 	if (pif == NULL)
3103431Scarlsonj 		pif = isv6 ? v6root : v4root;
3113431Scarlsonj 	else
3123431Scarlsonj 		pif = pif->pif_next;
3133431Scarlsonj 
3143431Scarlsonj 	for (; pif != NULL; pif = pif->pif_next) {
3153431Scarlsonj 		if ((pif->pif_index & 0xffff) == ifindex)
3163431Scarlsonj 			break;
3173431Scarlsonj 	}
3183431Scarlsonj 
3193431Scarlsonj 	return (pif);
3203431Scarlsonj }
3213431Scarlsonj 
3223431Scarlsonj /*
3233431Scarlsonj  * lookup_pif_by_name(): Looks up a physical interface entry given a name.
3243431Scarlsonj  *
3253431Scarlsonj  *   input: const char *: the physical interface name
3263431Scarlsonj  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
3273431Scarlsonj  *  output: dhcp_pif_t *: the matching PIF, or NULL if not found
3283431Scarlsonj  */
3293431Scarlsonj 
3303431Scarlsonj dhcp_pif_t *
3313431Scarlsonj lookup_pif_by_name(const char *pname, boolean_t isv6)
3323431Scarlsonj {
3333431Scarlsonj 	dhcp_pif_t *pif;
3343431Scarlsonj 
3353431Scarlsonj 	pif = isv6 ? v6root : v4root;
3363431Scarlsonj 
3373431Scarlsonj 	for (; pif != NULL; pif = pif->pif_next) {
3383431Scarlsonj 		if (strcmp(pif->pif_name, pname) == 0)
3393431Scarlsonj 			break;
3403431Scarlsonj 	}
3413431Scarlsonj 
3423431Scarlsonj 	return (pif);
3433431Scarlsonj }
3443431Scarlsonj 
3453431Scarlsonj /*
3463431Scarlsonj  * open_dlpi_pif(): register the use of DLPI I/O by a LIF on a PIF, opening
3473431Scarlsonj  *		    the connection if necessary.
3483431Scarlsonj  *
3493431Scarlsonj  *   input: dhcp_pif_t *: the physical interface on which to use DLPI
3503431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure.
3513431Scarlsonj  */
3523431Scarlsonj 
3533431Scarlsonj boolean_t
3543431Scarlsonj open_dlpi_pif(dhcp_pif_t *pif)
3553431Scarlsonj {
3564456Sss150715 	int		rc;
3574456Sss150715 	dlpi_handle_t	dh;
3583431Scarlsonj 
3594456Sss150715 	if (pif->pif_dlpi_hd == NULL) {
3604456Sss150715 		if ((rc = dlpi_open(pif->pif_name, &dh, 0)) != DLPI_SUCCESS) {
3614456Sss150715 			dhcpmsg(MSG_ERROR, "open_dlpi_pif: dlpi_open: %s",
3624456Sss150715 			    dlpi_strerror(rc));
3633431Scarlsonj 			return (B_FALSE);
3643431Scarlsonj 		}
3654456Sss150715 
3664456Sss150715 		if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) {
3674456Sss150715 			dhcpmsg(MSG_ERROR, "open_dlpi_pif: dlpi_bind: %s",
3684456Sss150715 			    dlpi_strerror(rc));
3694456Sss150715 			dlpi_close(dh);
3704456Sss150715 			return (B_FALSE);
3714456Sss150715 		}
3724456Sss150715 
3734456Sss150715 		if (!(set_packet_filter(dh, dhcp_filter, NULL, "DHCP"))) {
3744456Sss150715 			dlpi_close(dh);
3754456Sss150715 			return (B_FALSE);
3764456Sss150715 		}
3774456Sss150715 		pif->pif_dlpi_id = iu_register_event(eh, dlpi_fd(dh), POLLIN,
3784456Sss150715 		    dhcp_collect_dlpi, pif);
3794456Sss150715 		if (pif->pif_dlpi_id == -1) {
3804456Sss150715 			dlpi_close(dh);
3814456Sss150715 			return (B_FALSE);
3824456Sss150715 		}
3834456Sss150715 
3844456Sss150715 		pif->pif_dlpi_hd = dh;
3853431Scarlsonj 	}
3863431Scarlsonj 	pif->pif_dlpi_count++;
3873431Scarlsonj 	return (B_TRUE);
3883431Scarlsonj }
3893431Scarlsonj 
3903431Scarlsonj /*
3913431Scarlsonj  * close_dlpi_pif(): unregister the use of DLPI I/O by a LIF on a PIF, closing
3923431Scarlsonj  *		     the connection if this was the last user.
3933431Scarlsonj  *
3943431Scarlsonj  *   input: dhcp_pif_t *: the physical interface on which we're using DLPI
3953431Scarlsonj  *  output: none
3963431Scarlsonj  */
3973431Scarlsonj 
3983431Scarlsonj void
3993431Scarlsonj close_dlpi_pif(dhcp_pif_t *pif)
4003431Scarlsonj {
4013431Scarlsonj 	if (pif->pif_dlpi_count > 1) {
4023431Scarlsonj 		pif->pif_dlpi_count--;
4033431Scarlsonj 		return;
4043431Scarlsonj 	}
4053431Scarlsonj 	pif->pif_dlpi_count = 0;
4063431Scarlsonj 	if (pif->pif_dlpi_id != -1) {
4073431Scarlsonj 		(void) iu_unregister_event(eh, pif->pif_dlpi_id, NULL);
4083431Scarlsonj 		pif->pif_dlpi_id = -1;
4093431Scarlsonj 	}
4104456Sss150715 	if (pif->pif_dlpi_hd != NULL) {
4114456Sss150715 		dlpi_close(pif->pif_dlpi_hd);
4124456Sss150715 		pif->pif_dlpi_hd = NULL;
4133431Scarlsonj 	}
4143431Scarlsonj }
4153431Scarlsonj 
4163431Scarlsonj /*
4173431Scarlsonj  * pif_status(): update the physical interface up/down status.
4183431Scarlsonj  *
4193431Scarlsonj  *   input: dhcp_pif_t *: the physical interface on which we're using DLPI
4203431Scarlsonj  *	    boolean_t: B_TRUE if the interface is going up
4213431Scarlsonj  *  output: none
4223431Scarlsonj  */
4233431Scarlsonj 
4243431Scarlsonj void
4253431Scarlsonj pif_status(dhcp_pif_t *pif, boolean_t isup)
4263431Scarlsonj {
4273431Scarlsonj 	dhcp_lif_t *lif;
4283431Scarlsonj 	dhcp_smach_t *dsmp;
4293431Scarlsonj 
4303431Scarlsonj 	pif->pif_running = isup;
4314516Scarlsonj 	dhcpmsg(MSG_DEBUG, "interface %s has %s", pif->pif_name,
4323431Scarlsonj 	    isup ? "come back up" : "gone down");
4333431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
4343431Scarlsonj 		for (dsmp = lif->lif_smachs; dsmp != NULL;
4353431Scarlsonj 		    dsmp = dsmp->dsm_next) {
4363431Scarlsonj 			if (isup)
4373431Scarlsonj 				refresh_smach(dsmp);
4383431Scarlsonj 			else
4393431Scarlsonj 				remove_default_routes(dsmp);
4403431Scarlsonj 		}
4413431Scarlsonj 	}
4423431Scarlsonj }
4433431Scarlsonj 
4443431Scarlsonj /* Helper for insert_lif: extract addresses as defined */
4453431Scarlsonj #define	ASSIGN_ADDR(v4, v6, lf) \
4463431Scarlsonj 	if (pif->pif_isv6) { \
4473431Scarlsonj 		lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \
4483431Scarlsonj 	} else { \
4493431Scarlsonj 		lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \
4503431Scarlsonj 	}
4513431Scarlsonj 
4523431Scarlsonj /*
4533431Scarlsonj  * insert_lif(): Creates a new logical interface structure and chains it on
4543431Scarlsonj  *		 the list for a given physical interface.  Initializes state
4553431Scarlsonj  *		 that remains consistent across all use of the logical
4563431Scarlsonj  *		 interface entry.  Caller's PIF hold is transferred to the
4573431Scarlsonj  *		 LIF on success, and is dropped on failure.
4583431Scarlsonj  *
4593431Scarlsonj  *   input: dhcp_pif_t *: pointer to the physical interface for this LIF
4603431Scarlsonj  *	    const char *: the name of the logical interface
4613431Scarlsonj  *	    int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
4623431Scarlsonj  *		   error code with the reason why
4633431Scarlsonj  *  output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure
4643431Scarlsonj  */
4653431Scarlsonj 
4663431Scarlsonj dhcp_lif_t *
4673431Scarlsonj insert_lif(dhcp_pif_t *pif, const char *lname, int *error)
4683431Scarlsonj {
4693431Scarlsonj 	dhcp_lif_t *lif;
4703431Scarlsonj 	int fd;
4713431Scarlsonj 	struct lifreq lifr;
4723431Scarlsonj 
4733431Scarlsonj 	if ((lif = calloc(1, sizeof (*lif))) == NULL) {
4743431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for "
4753431Scarlsonj 		    "%s", lname);
4760Sstevel@tonic-gate 		*error = DHCP_IPC_E_MEMORY;
4770Sstevel@tonic-gate 		return (NULL);
4780Sstevel@tonic-gate 	}
4790Sstevel@tonic-gate 
4803431Scarlsonj 	lif->lif_sock_ip_fd = -1;
4813431Scarlsonj 	lif->lif_acknak_id = -1;
4823431Scarlsonj 	lif->lif_iaid_id = -1;
4833431Scarlsonj 	lif->lif_hold_count = 1;
4843431Scarlsonj 	lif->lif_pif = pif;
4853431Scarlsonj 	lif->lif_removed = B_TRUE;
4863431Scarlsonj 	init_timer(&lif->lif_preferred, 0);
4873431Scarlsonj 	init_timer(&lif->lif_expire, 0);
4880Sstevel@tonic-gate 
4893431Scarlsonj 	if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) {
4903431Scarlsonj 		dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long",
4913431Scarlsonj 		    lname);
4920Sstevel@tonic-gate 		*error = DHCP_IPC_E_INVIF;
4930Sstevel@tonic-gate 		goto failure;
4940Sstevel@tonic-gate 	}
4950Sstevel@tonic-gate 
4963431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ);
4973431Scarlsonj 
4983431Scarlsonj 	fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
4990Sstevel@tonic-gate 
5003431Scarlsonj 	if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1)
5013431Scarlsonj 		lif->lif_max = 1024;
5023431Scarlsonj 	else
5033431Scarlsonj 		lif->lif_max = lifr.lifr_mtu;
5040Sstevel@tonic-gate 
5053431Scarlsonj 	if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) {
5063431Scarlsonj 		if (errno == ENXIO)
5073431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
5083431Scarlsonj 		else
5093431Scarlsonj 			*error = DHCP_IPC_E_INT;
5103431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname);
5110Sstevel@tonic-gate 		goto failure;
5120Sstevel@tonic-gate 	}
5133431Scarlsonj 	ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr);
5140Sstevel@tonic-gate 
5153431Scarlsonj 	if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) {
5162546Scarlsonj 		if (errno == ENXIO)
5172546Scarlsonj 			*error = DHCP_IPC_E_INVIF;
5182546Scarlsonj 		else
5192546Scarlsonj 			*error = DHCP_IPC_E_INT;
5203431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname);
5213431Scarlsonj 		goto failure;
5223431Scarlsonj 	}
5233431Scarlsonj 	ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr);
5243431Scarlsonj 
5253431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
5263431Scarlsonj 		*error = DHCP_IPC_E_INT;
5273431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname);
5282546Scarlsonj 		goto failure;
5292546Scarlsonj 	}
5303431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
5313431Scarlsonj 
5323431Scarlsonj 	/*
5333431Scarlsonj 	 * If we've just detected the interface going up or down, then signal
5343431Scarlsonj 	 * an appropriate action.  There may be other state machines here.
5353431Scarlsonj 	 */
5363431Scarlsonj 	if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) {
5373431Scarlsonj 		pif_status(pif, B_TRUE);
5383431Scarlsonj 	} else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) {
5393431Scarlsonj 		pif_status(pif, B_FALSE);
5403431Scarlsonj 	}
5413431Scarlsonj 
5423431Scarlsonj 	if (lifr.lifr_flags & IFF_POINTOPOINT) {
5433431Scarlsonj 		if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) {
5443431Scarlsonj 			*error = DHCP_IPC_E_INT;
5453431Scarlsonj 			dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s",
5463431Scarlsonj 			    lname);
5473431Scarlsonj 			goto failure;
5483431Scarlsonj 		}
5493431Scarlsonj 		ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr);
5503431Scarlsonj 	} else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) {
5513431Scarlsonj 		if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) {
5523431Scarlsonj 			*error = DHCP_IPC_E_INT;
5533431Scarlsonj 			dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s",
5543431Scarlsonj 			    lname);
5553431Scarlsonj 			goto failure;
5563431Scarlsonj 		}
5573431Scarlsonj 		lif->lif_broadcast =
5583431Scarlsonj 		    ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr.
5593431Scarlsonj 		    s_addr;
5603431Scarlsonj 	}
5613431Scarlsonj 
5623431Scarlsonj 	if (pif->pif_isv6)
5633431Scarlsonj 		cached_v6_max_mtu = 0;
5643431Scarlsonj 	else
5653431Scarlsonj 		cached_v4_max_mtu = 0;
5663431Scarlsonj 
5673431Scarlsonj 	lif->lif_removed = B_FALSE;
5683431Scarlsonj 	insque(lif, &pif->pif_lifs);
5693431Scarlsonj 
5703431Scarlsonj 	return (lif);
5713431Scarlsonj 
5723431Scarlsonj failure:
5733431Scarlsonj 	release_lif(lif);
5743431Scarlsonj 	return (NULL);
5753431Scarlsonj }
5763431Scarlsonj 
5773431Scarlsonj /*
5783431Scarlsonj  * hold_lif(): acquire a hold on a logical interface structure.
5793431Scarlsonj  *
5803431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
5813431Scarlsonj  *  output: none
5823431Scarlsonj  */
5833431Scarlsonj 
5843431Scarlsonj void
5853431Scarlsonj hold_lif(dhcp_lif_t *lif)
5863431Scarlsonj {
5873431Scarlsonj 	lif->lif_hold_count++;
5883431Scarlsonj 	dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name,
5893431Scarlsonj 	    lif->lif_hold_count);
5903431Scarlsonj }
5913431Scarlsonj 
5923431Scarlsonj /*
5933431Scarlsonj  * release_lif(): release a hold on a logical interface structure; will
5943431Scarlsonj  *		  destroy the structure on the last hold removed.
5953431Scarlsonj  *
5963431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
5973431Scarlsonj  *  output: none
5983431Scarlsonj  */
5993431Scarlsonj 
6003431Scarlsonj void
6013431Scarlsonj release_lif(dhcp_lif_t *lif)
6023431Scarlsonj {
6033431Scarlsonj 	if (lif->lif_hold_count == 0) {
6043431Scarlsonj 		dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s",
6053431Scarlsonj 		    lif->lif_name);
6063431Scarlsonj 		return;
6073431Scarlsonj 	}
6083431Scarlsonj 
6093431Scarlsonj 	if (lif->lif_hold_count == 1 && !lif->lif_removed) {
6103431Scarlsonj 		unplumb_lif(lif);
6113431Scarlsonj 		return;
6123431Scarlsonj 	}
6133431Scarlsonj 
6143431Scarlsonj 	if (--lif->lif_hold_count == 0) {
6153431Scarlsonj 		dhcp_pif_t *pif;
6163431Scarlsonj 
6173431Scarlsonj 		dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s",
6183431Scarlsonj 		    lif->lif_name);
6193431Scarlsonj 
6203431Scarlsonj 		if (lif->lif_lease != NULL)
6213431Scarlsonj 			dhcpmsg(MSG_CRIT,
6223431Scarlsonj 			    "release_lif: still holding lease at last hold!");
6233431Scarlsonj 		close_ip_lif(lif);
6243431Scarlsonj 		pif = lif->lif_pif;
6253431Scarlsonj 		if (pif->pif_isv6)
6263431Scarlsonj 			cached_v6_max_mtu = 0;
6273431Scarlsonj 		else
6283431Scarlsonj 			cached_v4_max_mtu = 0;
6293431Scarlsonj 		release_pif(pif);
6303431Scarlsonj 		free(lif);
6313431Scarlsonj 	} else {
6323431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u",
6333431Scarlsonj 		    lif->lif_name, lif->lif_hold_count);
6343431Scarlsonj 	}
6353431Scarlsonj }
6363431Scarlsonj 
6373431Scarlsonj /*
6383431Scarlsonj  * remove_lif(): remove a logical interface from its PIF and lease (if any) and
6393431Scarlsonj  *		 the lease's hold on the LIF.  Assumes that we did not plumb
6403431Scarlsonj  *		 the interface.
6413431Scarlsonj  *
6423431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
6433431Scarlsonj  *  output: none
6443431Scarlsonj  */
6453431Scarlsonj 
6463431Scarlsonj void
6473431Scarlsonj remove_lif(dhcp_lif_t *lif)
6483431Scarlsonj {
6493431Scarlsonj 	if (lif->lif_plumbed) {
6503431Scarlsonj 		dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s",
6513431Scarlsonj 		    lif->lif_name);
6523431Scarlsonj 		return;
6533431Scarlsonj 	}
6543431Scarlsonj 	if (lif->lif_removed) {
6553431Scarlsonj 		dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s",
6563431Scarlsonj 		    lif->lif_name);
6573431Scarlsonj 	} else {
6583431Scarlsonj 		dhcp_lif_t *lifnext;
6593431Scarlsonj 		dhcp_lease_t *dlp;
6603431Scarlsonj 
6613431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name);
6623431Scarlsonj 		lif->lif_removed = B_TRUE;
6633431Scarlsonj 		lifnext = lif->lif_next;
6643431Scarlsonj 		clear_lif_dhcp(lif);
6653431Scarlsonj 		cancel_lif_timers(lif);
6663431Scarlsonj 		if (lif->lif_iaid_id != -1 &&
6673431Scarlsonj 		    iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) {
6683431Scarlsonj 			lif->lif_iaid_id = -1;
6693431Scarlsonj 			release_lif(lif);
6703431Scarlsonj 		}
6713431Scarlsonj 
6723431Scarlsonj 		/* Remove from PIF list */
6733431Scarlsonj 		remque(lif);
6743431Scarlsonj 
6753431Scarlsonj 		/* If we were part of a lease, then remove ourselves */
6763431Scarlsonj 		if ((dlp = lif->lif_lease) != NULL) {
6773431Scarlsonj 			if (--dlp->dl_nlifs == 0)
6783431Scarlsonj 				dlp->dl_lifs = NULL;
6793431Scarlsonj 			else if (dlp->dl_lifs == lif)
6803431Scarlsonj 				dlp->dl_lifs = lifnext;
6813431Scarlsonj 			if (lif->lif_flags & IFF_DHCPRUNNING)
6823431Scarlsonj 				clear_lif_dhcp(lif);
6833431Scarlsonj 			if (lif->lif_declined != NULL) {
6843431Scarlsonj 				dlp->dl_smach->dsm_lif_down--;
6853431Scarlsonj 				lif->lif_declined = NULL;
6863431Scarlsonj 			}
6873431Scarlsonj 			lif->lif_lease = NULL;
6883431Scarlsonj 			release_lif(lif);
6893431Scarlsonj 		}
6903431Scarlsonj 	}
6913431Scarlsonj }
6923431Scarlsonj 
6933431Scarlsonj /*
6943431Scarlsonj  * lookup_lif_by_name(): Looks up a logical interface entry given a name and
6953431Scarlsonj  *			 a physical interface.
6963431Scarlsonj  *
6973431Scarlsonj  *   input: const char *: the logical interface name
6983431Scarlsonj  *	    const dhcp_pif_t *: the physical interface
6993431Scarlsonj  *  output: dhcp_lif_t *: the matching LIF, or NULL if not found
7003431Scarlsonj  */
7013431Scarlsonj 
7023431Scarlsonj dhcp_lif_t *
7033431Scarlsonj lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif)
7043431Scarlsonj {
7053431Scarlsonj 	dhcp_lif_t *lif;
7063431Scarlsonj 
7073431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
7083431Scarlsonj 		if (strcmp(lif->lif_name, lname) == 0)
7093431Scarlsonj 			break;
7103431Scarlsonj 	}
7113431Scarlsonj 
7123431Scarlsonj 	return (lif);
7133431Scarlsonj }
7143431Scarlsonj 
7153431Scarlsonj /*
7163431Scarlsonj  * checkaddr(): checks if the given address is still set on the given LIF
7173431Scarlsonj  *
7183431Scarlsonj  *   input: const dhcp_lif_t *: the LIF to check
7193431Scarlsonj  *	    int: the address to look up on the interface (ioctl)
7203431Scarlsonj  *	    const in6_addr_t *: the address to compare to
7213431Scarlsonj  *	    const char *: name of the address for logging purposes
7223431Scarlsonj  *  output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
7233431Scarlsonj  */
7243431Scarlsonj 
7253431Scarlsonj static boolean_t
7263431Scarlsonj checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr,
7273431Scarlsonj     const char *aname)
7283431Scarlsonj {
7293431Scarlsonj 	boolean_t isv6;
7303431Scarlsonj 	int fd;
7313431Scarlsonj 	struct lifreq lifr;
7323431Scarlsonj 
7333431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
7343431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
7353431Scarlsonj 
7363431Scarlsonj 	isv6 = lif->lif_pif->pif_isv6;
7373431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
7383431Scarlsonj 
7393431Scarlsonj 	if (ioctl(fd, ioccmd, &lifr) == -1) {
7403431Scarlsonj 		if (errno == ENXIO) {
7413431Scarlsonj 			dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone",
7423431Scarlsonj 			    lif->lif_name);
7433431Scarlsonj 			return (B_FALSE);
7443431Scarlsonj 		}
7453431Scarlsonj 		dhcpmsg(MSG_DEBUG,
7463431Scarlsonj 		    "checkaddr: ignoring ioctl error on %s %x: %s",
7473431Scarlsonj 		    lif->lif_name, ioccmd, strerror(errno));
7483431Scarlsonj 	} else if (isv6) {
7493431Scarlsonj 		struct sockaddr_in6 *sin6 =
7503431Scarlsonj 		    (struct sockaddr_in6 *)&lifr.lifr_addr;
7513431Scarlsonj 		char abuf1[INET6_ADDRSTRLEN];
7523431Scarlsonj 		char abuf2[INET6_ADDRSTRLEN];
7533431Scarlsonj 
7543431Scarlsonj 		if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) {
7553431Scarlsonj 			dhcpmsg(MSG_WARNING,
7563431Scarlsonj 			    "checkaddr: expected %s %s on %s, have %s",
7573431Scarlsonj 			    aname, inet_ntop(AF_INET6, &sin6->sin6_addr, abuf1,
7583431Scarlsonj 			    sizeof (abuf1)),  lif->lif_name,
7593431Scarlsonj 			    inet_ntop(AF_INET6, addr, abuf2, sizeof (abuf2)));
7603431Scarlsonj 			return (B_FALSE);
7613431Scarlsonj 		}
7623431Scarlsonj 	} else {
7633431Scarlsonj 		struct sockaddr_in *sinp =
7643431Scarlsonj 		    (struct sockaddr_in *)&lifr.lifr_addr;
7653431Scarlsonj 		ipaddr_t v4addr;
7663431Scarlsonj 		char abuf1[INET_ADDRSTRLEN];
7673431Scarlsonj 		char abuf2[INET_ADDRSTRLEN];
7683431Scarlsonj 
7693431Scarlsonj 		IN6_V4MAPPED_TO_IPADDR(addr, v4addr);
7703431Scarlsonj 		if (sinp->sin_addr.s_addr != v4addr) {
7713431Scarlsonj 			dhcpmsg(MSG_WARNING,
7723431Scarlsonj 			    "checkaddr: expected %s %s on %s, have %s",
7733431Scarlsonj 			    aname, inet_ntop(AF_INET, &sinp->sin_addr, abuf1,
7743431Scarlsonj 			    sizeof (abuf1)),  lif->lif_name,
7753431Scarlsonj 			    inet_ntop(AF_INET, &v4addr, abuf2,
7763431Scarlsonj 			    sizeof (abuf2)));
7773431Scarlsonj 			return (B_FALSE);
7783431Scarlsonj 		}
7793431Scarlsonj 	}
7803431Scarlsonj 	return (B_TRUE);
7813431Scarlsonj }
7823431Scarlsonj 
7833431Scarlsonj /*
7843431Scarlsonj  * verify_lif(): verifies than a LIF is still valid (i.e., has not been
7853431Scarlsonj  *		 explicitly or implicitly dropped or released)
7863431Scarlsonj  *
7873431Scarlsonj  *   input: const dhcp_lif_t *: the LIF to verify
7883431Scarlsonj  *  output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise
7893431Scarlsonj  */
7903431Scarlsonj 
7913431Scarlsonj boolean_t
7923431Scarlsonj verify_lif(const dhcp_lif_t *lif)
7933431Scarlsonj {
7943431Scarlsonj 	boolean_t isv6;
7953431Scarlsonj 	int fd;
7963431Scarlsonj 	struct lifreq lifr;
7973431Scarlsonj 
7983431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
7993431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
8003431Scarlsonj 
8013431Scarlsonj 	isv6 = lif->lif_pif->pif_isv6;
8023431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
8033431Scarlsonj 
8043431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
8054106Scarlsonj 		if (errno != ENXIO) {
8064106Scarlsonj 			dhcpmsg(MSG_ERR,
8074106Scarlsonj 			    "verify_lif: SIOCGLIFFLAGS failed on %s",
8084106Scarlsonj 			    lif->lif_name);
8094106Scarlsonj 		}
8103431Scarlsonj 		return (B_FALSE);
8113431Scarlsonj 	}
8123431Scarlsonj 
8133431Scarlsonj 	/*
8143431Scarlsonj 	 * If important flags have changed, then abandon the interface.
8153431Scarlsonj 	 */
8163431Scarlsonj 	if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) {
8173431Scarlsonj 		dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: "
8183431Scarlsonj 		    "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags,
8193431Scarlsonj 		    lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) &
8203431Scarlsonj 		    DHCP_IFF_WATCH);
8213431Scarlsonj 		return (B_FALSE);
8223431Scarlsonj 	}
8233431Scarlsonj 
8243431Scarlsonj 	/*
8253431Scarlsonj 	 * Special case: if the interface has gone down as a duplicate, then
8263431Scarlsonj 	 * this alone does _not_ mean that we're abandoning it just yet.  Allow
8273431Scarlsonj 	 * the state machine to handle this normally by trying to get a new
8283431Scarlsonj 	 * lease.
8293431Scarlsonj 	 */
8303431Scarlsonj 	if ((lifr.lifr_flags & (IFF_UP|IFF_DUPLICATE)) == IFF_DUPLICATE) {
8313431Scarlsonj 		dhcpmsg(MSG_DEBUG, "verify_lif: duplicate address on %s",
8323431Scarlsonj 		    lif->lif_name);
8333431Scarlsonj 		return (B_TRUE);
8343431Scarlsonj 	}
8353431Scarlsonj 
8363431Scarlsonj 	/*
8373431Scarlsonj 	 * If the user has torn down or started up the interface manually, then
8383431Scarlsonj 	 * abandon the lease.
8393431Scarlsonj 	 */
8403431Scarlsonj 	if ((lif->lif_flags ^ lifr.lifr_flags) & IFF_UP) {
8413431Scarlsonj 		dhcpmsg(MSG_DEBUG, "verify_lif: user has %s %s",
8423431Scarlsonj 		    lifr.lifr_flags & IFF_UP ? "started up" : "shut down",
8433431Scarlsonj 		    lif->lif_name);
8443431Scarlsonj 		return (B_FALSE);
8453431Scarlsonj 	}
8463431Scarlsonj 
8473431Scarlsonj 	/*
8483431Scarlsonj 	 * Check for delete and recreate.
8493431Scarlsonj 	 */
8503431Scarlsonj 	if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
8513431Scarlsonj 		dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed on %s",
8523431Scarlsonj 		    lif->lif_name);
8533431Scarlsonj 		return (B_FALSE);
8543431Scarlsonj 	}
8553431Scarlsonj 	if (lifr.lifr_index != lif->lif_pif->pif_index) {
8563431Scarlsonj 		dhcpmsg(MSG_DEBUG,
8573431Scarlsonj 		    "verify_lif: ifindex on %s changed: %u to %u",
8583431Scarlsonj 		    lif->lif_name, lif->lif_pif->pif_index, lifr.lifr_index);
8593431Scarlsonj 		return (B_FALSE);
8603431Scarlsonj 	}
8613431Scarlsonj 
8623431Scarlsonj 	/*
8633431Scarlsonj 	 * If the IP address, netmask, or broadcast address have changed, or
8643431Scarlsonj 	 * the interface has been unplumbed, then we act like there has been an
8653431Scarlsonj 	 * implicit drop.  (Note that the netmask is under DHCP control for
8663431Scarlsonj 	 * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast
8673431Scarlsonj 	 * addresses.)
8683431Scarlsonj 	 */
8692546Scarlsonj 
8703431Scarlsonj 	if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address"))
8713431Scarlsonj 		return (B_FALSE);
8723431Scarlsonj 
8733431Scarlsonj 	if (isv6) {
8743431Scarlsonj 		/*
8753431Scarlsonj 		 * If it's not point-to-point, we're done.  If it is, then
8763431Scarlsonj 		 * check the peer's address as well.
8773431Scarlsonj 		 */
8783431Scarlsonj 		return (!(lif->lif_flags & IFF_POINTOPOINT) ||
8793431Scarlsonj 		    checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer,
8803431Scarlsonj 		    "peer address"));
8813431Scarlsonj 	} else {
8823431Scarlsonj 		if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask,
8833431Scarlsonj 		    "netmask"))
8843431Scarlsonj 			return (B_FALSE);
8853431Scarlsonj 
8863431Scarlsonj 		return (checkaddr(lif,
8873431Scarlsonj 		    (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR :
8883431Scarlsonj 		    SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address"));
8893431Scarlsonj 	}
8903431Scarlsonj }
8913431Scarlsonj 
8923431Scarlsonj /*
8933431Scarlsonj  * canonize_lif(): puts the interface in a canonical (zeroed) form.  This is
8943431Scarlsonj  *		   used only on the "main" LIF for IPv4.  All other interfaces
8953431Scarlsonj  *		   are under dhcpagent control and are removed using
8963431Scarlsonj  *		   unplumb_lif().
8973431Scarlsonj  *
8983431Scarlsonj  *   input: dhcp_lif_t *: the interface to canonize
8993431Scarlsonj  *  output: none
9003431Scarlsonj  */
9013431Scarlsonj 
9023431Scarlsonj static void
9033431Scarlsonj canonize_lif(dhcp_lif_t *lif)
9043431Scarlsonj {
9053431Scarlsonj 	boolean_t isv6;
9063431Scarlsonj 	int fd;
9073431Scarlsonj 	struct lifreq lifr;
9083431Scarlsonj 
9093431Scarlsonj 	/*
9103431Scarlsonj 	 * If there's nothing here, then don't touch the interface.  This can
9113431Scarlsonj 	 * happen when an already-canonized LIF is recanonized.
9123431Scarlsonj 	 */
9133431Scarlsonj 	if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr))
9143431Scarlsonj 		return;
9153431Scarlsonj 
9163431Scarlsonj 	isv6 = lif->lif_pif->pif_isv6;
9173431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s",
9183431Scarlsonj 	    isv6 ? 6 : 4, lif->lif_name);
9193431Scarlsonj 
9203431Scarlsonj 	lif->lif_v6addr = my_in6addr_any;
9213431Scarlsonj 	lif->lif_v6mask = my_in6addr_any;
9223431Scarlsonj 	lif->lif_v6peer = my_in6addr_any;
9233431Scarlsonj 
9243431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
9253431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
9263431Scarlsonj 
9273431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
9283431Scarlsonj 
9293431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
9304106Scarlsonj 		if (errno != ENXIO) {
9314106Scarlsonj 			dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s",
9324106Scarlsonj 			    lif->lif_name);
9334106Scarlsonj 		}
9343431Scarlsonj 		return;
9353431Scarlsonj 	}
9363431Scarlsonj 
9373431Scarlsonj 	/* Should not happen */
9383431Scarlsonj 	if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) {
9393431Scarlsonj 		dhcpmsg(MSG_INFO,
9403431Scarlsonj 		    "canonize_lif: cannot clear %s; flags are %llx",
9413431Scarlsonj 		    lif->lif_name, lifr.lifr_flags);
9423431Scarlsonj 		return;
9433431Scarlsonj 	}
9443431Scarlsonj 
9453431Scarlsonj 	/*
9463431Scarlsonj 	 * clear the UP flag, but don't clear DHCPRUNNING since
9473431Scarlsonj 	 * that should only be done when the interface is removed
9483431Scarlsonj 	 * (see clear_lif_dhcp() and remove_lif())
9493431Scarlsonj 	 */
9503431Scarlsonj 
9513431Scarlsonj 	lif->lif_flags = lifr.lifr_flags &= ~IFF_UP;
9523431Scarlsonj 
9533431Scarlsonj 	if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
9543431Scarlsonj 		dhcpmsg(MSG_ERR, "canonize_lif: can't set flags for %s",
9553431Scarlsonj 		    lif->lif_name);
9563431Scarlsonj 		return;
9573431Scarlsonj 	}
9583431Scarlsonj 
9593431Scarlsonj 	(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
9603431Scarlsonj 	if (isv6) {
9613431Scarlsonj 		struct sockaddr_in6 *sin6 =
9623431Scarlsonj 		    (struct sockaddr_in6 *)&lifr.lifr_addr;
9633431Scarlsonj 
9643431Scarlsonj 		sin6->sin6_family = AF_INET6;
9653431Scarlsonj 		sin6->sin6_addr = my_in6addr_any;
9663431Scarlsonj 	} else {
9673431Scarlsonj 		struct sockaddr_in *sinv =
9683431Scarlsonj 		    (struct sockaddr_in *)&lifr.lifr_addr;
9693431Scarlsonj 
9703431Scarlsonj 		sinv->sin_family = AF_INET;
9713431Scarlsonj 		sinv->sin_addr.s_addr = htonl(INADDR_ANY);
9723431Scarlsonj 	}
9733431Scarlsonj 
9743431Scarlsonj 	if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) {
9753431Scarlsonj 		dhcpmsg(MSG_ERR,
9763431Scarlsonj 		    "canonize_lif: can't clear local address on %s",
9773431Scarlsonj 		    lif->lif_name);
9783431Scarlsonj 	}
9793431Scarlsonj 
9803431Scarlsonj 	/* Netmask is under in.ndpd control with IPv6 */
9813431Scarlsonj 	if (!isv6 && ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) {
9823431Scarlsonj 		dhcpmsg(MSG_ERR, "canonize_lif: can't clear netmask on %s",
9833431Scarlsonj 		    lif->lif_name);
9843431Scarlsonj 	}
9853431Scarlsonj 
9863431Scarlsonj 	if (lif->lif_flags & IFF_POINTOPOINT) {
9873431Scarlsonj 		if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) {
9883431Scarlsonj 			dhcpmsg(MSG_ERR,
9893431Scarlsonj 			    "canonize_lif: can't clear remote address on %s",
9903431Scarlsonj 			    lif->lif_name);
9913431Scarlsonj 		}
9923431Scarlsonj 	} else if (!isv6) {
9933431Scarlsonj 		if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) {
9943431Scarlsonj 			dhcpmsg(MSG_ERR,
9953431Scarlsonj 			    "canonize_lif: can't clear broadcast address on %s",
9963431Scarlsonj 			    lif->lif_name);
9973431Scarlsonj 		}
9983431Scarlsonj 	}
9993431Scarlsonj }
10003431Scarlsonj 
10013431Scarlsonj /*
10023431Scarlsonj  * plumb_lif(): Adds the LIF to the system.  This is used for all
10033431Scarlsonj  *		DHCPv6-derived interfaces.  The returned LIF has a hold
10043431Scarlsonj  *		on it.
10053431Scarlsonj  *
10063431Scarlsonj  *   input: dhcp_lif_t *: the interface to unplumb
10073431Scarlsonj  *  output: none
10083431Scarlsonj  */
10093431Scarlsonj 
10103431Scarlsonj dhcp_lif_t *
10113431Scarlsonj plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr)
10123431Scarlsonj {
10133431Scarlsonj 	dhcp_lif_t *lif;
10143431Scarlsonj 	char abuf[INET6_ADDRSTRLEN];
10153431Scarlsonj 	struct lifreq lifr;
10163431Scarlsonj 	struct sockaddr_in6 *sin6;
10173431Scarlsonj 	int error;
10183431Scarlsonj 
10193431Scarlsonj 	(void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf));
10203431Scarlsonj 
10213431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
10223431Scarlsonj 		if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) {
10233431Scarlsonj 			dhcpmsg(MSG_ERR,
10243431Scarlsonj 			    "plumb_lif: entry for %s already exists!", abuf);
10253431Scarlsonj 			return (NULL);
10263431Scarlsonj 		}
10273431Scarlsonj 	}
10283431Scarlsonj 
10293431Scarlsonj 	/* First, create a new zero-address logical interface */
10303431Scarlsonj 	(void) memset(&lifr, 0, sizeof (lifr));
10313431Scarlsonj 	(void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name));
10323431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) {
10333431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name);
10343431Scarlsonj 		return (NULL);
10353431Scarlsonj 	}
10363431Scarlsonj 
10373431Scarlsonj 	/* Next, set the netmask to all ones */
10383431Scarlsonj 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
10393431Scarlsonj 	sin6->sin6_family = AF_INET6;
10403431Scarlsonj 	(void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr));
10413431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
10423431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s",
10433431Scarlsonj 		    lifr.lifr_name);
10440Sstevel@tonic-gate 		goto failure;
10450Sstevel@tonic-gate 	}
10460Sstevel@tonic-gate 
10473431Scarlsonj 	/* Now set the interface address */
10483431Scarlsonj 	sin6->sin6_addr = *addr;
10493431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
10503431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s",
10513431Scarlsonj 		    lifr.lifr_name, abuf);
10523431Scarlsonj 		goto failure;
10533431Scarlsonj 	}
10543431Scarlsonj 
10553431Scarlsonj 	/* Mark the interface up */
10563431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
10573431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s",
10583431Scarlsonj 		    lifr.lifr_name);
10593431Scarlsonj 		goto failure;
10603431Scarlsonj 	}
10613431Scarlsonj 	lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING;
10623431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
10633431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s",
10643431Scarlsonj 		    lifr.lifr_name);
10653431Scarlsonj 		goto failure;
10663431Scarlsonj 	}
10673431Scarlsonj 
10683431Scarlsonj 	/* Now we can create the internal LIF structure */
10693431Scarlsonj 	hold_pif(pif);
10703431Scarlsonj 	if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL)
10713431Scarlsonj 		goto failure;
10723431Scarlsonj 
10733431Scarlsonj 	dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf,
10743431Scarlsonj 	    lif->lif_name);
10753431Scarlsonj 	lif->lif_plumbed = B_TRUE;
10763431Scarlsonj 
10773431Scarlsonj 	return (lif);
10783431Scarlsonj 
10793431Scarlsonj failure:
10803431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
10813431Scarlsonj 	    errno != ENXIO) {
10823431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s",
10833431Scarlsonj 		    lifr.lifr_name);
10843431Scarlsonj 	}
10853431Scarlsonj 	return (NULL);
10863431Scarlsonj }
10873431Scarlsonj 
10883431Scarlsonj /*
10893431Scarlsonj  * unplumb_lif(): Removes the LIF from dhcpagent and the system.  This is used
10903431Scarlsonj  *		  for all interfaces configured by DHCP (those in leases).
10913431Scarlsonj  *
10923431Scarlsonj  *   input: dhcp_lif_t *: the interface to unplumb
10933431Scarlsonj  *  output: none
10943431Scarlsonj  */
10953431Scarlsonj 
10963431Scarlsonj void
10973431Scarlsonj unplumb_lif(dhcp_lif_t *lif)
10983431Scarlsonj {
10993431Scarlsonj 	dhcp_lease_t *dlp;
11003431Scarlsonj 
11013431Scarlsonj 	if (lif->lif_plumbed) {
11023431Scarlsonj 		struct lifreq lifr;
11033431Scarlsonj 
11043431Scarlsonj 		(void) memset(&lifr, 0, sizeof (lifr));
11053431Scarlsonj 		(void) strlcpy(lifr.lifr_name, lif->lif_name,
11063431Scarlsonj 		    sizeof (lifr.lifr_name));
11073431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
11083431Scarlsonj 		    errno != ENXIO) {
11093431Scarlsonj 			dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s",
11103431Scarlsonj 			    lif->lif_name);
11113431Scarlsonj 		}
11123431Scarlsonj 		lif->lif_plumbed = B_FALSE;
11133431Scarlsonj 	}
11143431Scarlsonj 	lif->lif_flags = 0;
11153431Scarlsonj 	/*
11163431Scarlsonj 	 * Special case: if we're "unplumbing" the main LIF for DHCPv4, then
11173431Scarlsonj 	 * just canonize it and remove it from the lease.
11183431Scarlsonj 	 */
11193431Scarlsonj 	if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) {
11203431Scarlsonj 		canonize_lif(lif);
11213431Scarlsonj 		cancel_lif_timers(lif);
11223431Scarlsonj 		if (lif->lif_declined != NULL) {
11233431Scarlsonj 			dlp->dl_smach->dsm_lif_down--;
11243431Scarlsonj 			lif->lif_declined = NULL;
11253431Scarlsonj 		}
11263431Scarlsonj 		dlp->dl_nlifs = 0;
11273431Scarlsonj 		dlp->dl_lifs = NULL;
11283431Scarlsonj 		lif->lif_lease = NULL;
11293431Scarlsonj 		release_lif(lif);
11303431Scarlsonj 	} else {
11313431Scarlsonj 		remove_lif(lif);
11323431Scarlsonj 	}
11333431Scarlsonj }
11343431Scarlsonj 
11353431Scarlsonj /*
11363431Scarlsonj  * attach_lif(): create a new logical interface, creating the physical
11373431Scarlsonj  *		 interface as necessary.
11383431Scarlsonj  *
11393431Scarlsonj  *   input: const char *: the logical interface name
11403431Scarlsonj  *	    boolean_t: B_TRUE for IPv6
11413431Scarlsonj  *	    int *: set to DHCP_IPC_E_* if creation fails
11423431Scarlsonj  *  output: dhcp_lif_t *: pointer to new entry, or NULL on failure
11433431Scarlsonj  */
11443431Scarlsonj 
11453431Scarlsonj dhcp_lif_t *
11463431Scarlsonj attach_lif(const char *lname, boolean_t isv6, int *error)
11473431Scarlsonj {
11483431Scarlsonj 	dhcp_pif_t *pif;
11493431Scarlsonj 	char pname[LIFNAMSIZ], *cp;
11503431Scarlsonj 
11513431Scarlsonj 	(void) strlcpy(pname, lname, sizeof (pname));
11523431Scarlsonj 	if ((cp = strchr(pname, ':')) != NULL)
11533431Scarlsonj 		*cp = '\0';
11543431Scarlsonj 
11553431Scarlsonj 	if ((pif = lookup_pif_by_name(pname, isv6)) != NULL)
11563431Scarlsonj 		hold_pif(pif);
11573431Scarlsonj 	else if ((pif = insert_pif(pname, isv6, error)) == NULL)
11583431Scarlsonj 		return (NULL);
11593431Scarlsonj 
11603431Scarlsonj 	if (lookup_lif_by_name(lname, pif) != NULL) {
11613431Scarlsonj 		dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!",
11623431Scarlsonj 		    lname);
11633431Scarlsonj 		release_pif(pif);
11643431Scarlsonj 		*error = DHCP_IPC_E_INVIF;
11653431Scarlsonj 		return (NULL);
11663431Scarlsonj 	}
11673431Scarlsonj 
11683431Scarlsonj 	/* If LIF creation fails, then insert_lif discards our PIF hold */
11693431Scarlsonj 	return (insert_lif(pif, lname, error));
11703431Scarlsonj }
11713431Scarlsonj 
11723431Scarlsonj /*
11733431Scarlsonj  * set_lif_dhcp(): Set logical interface flags to show that it's managed
11743431Scarlsonj  *		   by DHCP.
11753431Scarlsonj  *
11763431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
11773431Scarlsonj  *	    boolean_t: B_TRUE if adopting
11783431Scarlsonj  *  output: int: set to DHCP_IPC_E_* if operation fails
11793431Scarlsonj  */
11803431Scarlsonj 
11813431Scarlsonj int
11823431Scarlsonj set_lif_dhcp(dhcp_lif_t *lif, boolean_t is_adopting)
11833431Scarlsonj {
11843431Scarlsonj 	int fd;
11853431Scarlsonj 	int err;
11863431Scarlsonj 	struct lifreq lifr;
11873431Scarlsonj 
11883431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
11893431Scarlsonj 
11903431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
11913431Scarlsonj 
11923431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
11933431Scarlsonj 		err = errno;
11943431Scarlsonj 		dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s",
11953431Scarlsonj 		    lif->lif_name);
11963431Scarlsonj 		return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT);
11973431Scarlsonj 	}
11983431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
11993431Scarlsonj 
12003431Scarlsonj 	/*
12013431Scarlsonj 	 * Check for conflicting sources of address control, and other
12023431Scarlsonj 	 * unacceptable configurations.
12033431Scarlsonj 	 */
1204*4823Sseb 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
1205*4823Sseb 	    IFF_VIRTUAL)) {
12063431Scarlsonj 		dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx",
12073431Scarlsonj 		    lif->lif_name, lifr.lifr_flags);
12083431Scarlsonj 		return (DHCP_IPC_E_INVIF);
12093431Scarlsonj 	}
12103431Scarlsonj 
12110Sstevel@tonic-gate 	/*
12120Sstevel@tonic-gate 	 * if DHCPRUNNING is already set on the interface and we're
12130Sstevel@tonic-gate 	 * not adopting it, the agent probably crashed and burned.
12140Sstevel@tonic-gate 	 * note it, but don't let it stop the proceedings.  we're
12150Sstevel@tonic-gate 	 * pretty sure we're not already running, since we wouldn't
12160Sstevel@tonic-gate 	 * have been able to bind to our IPC port.
12170Sstevel@tonic-gate 	 */
12180Sstevel@tonic-gate 
12193431Scarlsonj 	if (lifr.lifr_flags & IFF_DHCPRUNNING) {
12203431Scarlsonj 		if (!is_adopting) {
12213431Scarlsonj 			dhcpmsg(MSG_WARNING, "set_lif_dhcp: DHCP flag already "
12223431Scarlsonj 			    "set on %s", lif->lif_name);
12233431Scarlsonj 		}
12243431Scarlsonj 	} else {
12253431Scarlsonj 		lifr.lifr_flags |= IFF_DHCPRUNNING;
12263431Scarlsonj 		if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
12273431Scarlsonj 			dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s",
12283431Scarlsonj 			    lif->lif_name);
12293431Scarlsonj 			return (DHCP_IPC_E_INT);
12303431Scarlsonj 		}
12313431Scarlsonj 		lif->lif_flags = lifr.lifr_flags;
12320Sstevel@tonic-gate 	}
12333431Scarlsonj 	return (DHCP_IPC_SUCCESS);
12343431Scarlsonj }
12350Sstevel@tonic-gate 
12363431Scarlsonj /*
12373431Scarlsonj  * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer
12383431Scarlsonj  *		     managed by DHCP.
12393431Scarlsonj  *
12403431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
12413431Scarlsonj  *  output: none
12423431Scarlsonj  */
12430Sstevel@tonic-gate 
12443431Scarlsonj static void
12453431Scarlsonj clear_lif_dhcp(dhcp_lif_t *lif)
12463431Scarlsonj {
12473431Scarlsonj 	int fd;
12483431Scarlsonj 	struct lifreq lifr;
12490Sstevel@tonic-gate 
12503431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
12513431Scarlsonj 
12523431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
12533431Scarlsonj 
12543431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
12553431Scarlsonj 		return;
12560Sstevel@tonic-gate 
12573431Scarlsonj 	if (!(lifr.lifr_flags & IFF_DHCPRUNNING))
12583431Scarlsonj 		return;
12590Sstevel@tonic-gate 
12603431Scarlsonj 	lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING;
12613431Scarlsonj 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
12623431Scarlsonj }
12630Sstevel@tonic-gate 
12643431Scarlsonj /*
12653431Scarlsonj  * set_lif_deprecated(): Set the "deprecated" flag to tell users that this
12663431Scarlsonj  *			 address will be going away.  As the interface is
12673431Scarlsonj  *			 going away, we don't care if there are errors.
12683431Scarlsonj  *
12693431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
12703431Scarlsonj  *  output: none
12713431Scarlsonj  */
12720Sstevel@tonic-gate 
12733431Scarlsonj void
12743431Scarlsonj set_lif_deprecated(dhcp_lif_t *lif)
12753431Scarlsonj {
12763431Scarlsonj 	int fd;
12773431Scarlsonj 	struct lifreq lifr;
12780Sstevel@tonic-gate 
12793431Scarlsonj 	if (lif->lif_flags & IFF_DEPRECATED)
12803431Scarlsonj 		return;
12810Sstevel@tonic-gate 
12823431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
12833431Scarlsonj 
12843431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
12850Sstevel@tonic-gate 
12863431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
12873431Scarlsonj 		return;
12883431Scarlsonj 
12893431Scarlsonj 	if (lifr.lifr_flags & IFF_DEPRECATED)
12903431Scarlsonj 		return;
12910Sstevel@tonic-gate 
12923431Scarlsonj 	lifr.lifr_flags |= IFF_DEPRECATED;
12933431Scarlsonj 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
12943431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
12953431Scarlsonj }
12963431Scarlsonj 
12973431Scarlsonj /*
12983431Scarlsonj  * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this
12993431Scarlsonj  *			   address will not be going away.  This happens if we
13003431Scarlsonj  *			   get a renewal after preferred lifetime but before
13013431Scarlsonj  *			   the valid lifetime.
13023431Scarlsonj  *
13033431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
13043431Scarlsonj  *  output: boolean_t: B_TRUE on success.
13053431Scarlsonj  */
13060Sstevel@tonic-gate 
13073431Scarlsonj boolean_t
13083431Scarlsonj clear_lif_deprecated(dhcp_lif_t *lif)
13093431Scarlsonj {
13103431Scarlsonj 	int fd;
13113431Scarlsonj 	struct lifreq lifr;
13123431Scarlsonj 
13133431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
13143431Scarlsonj 
13153431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
13163431Scarlsonj 
13173431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
13183431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s",
13193431Scarlsonj 		    lif->lif_name);
13203431Scarlsonj 		return (B_FALSE);
13210Sstevel@tonic-gate 	}
13220Sstevel@tonic-gate 
13230Sstevel@tonic-gate 	/*
13243431Scarlsonj 	 * Check for conflicting sources of address control, and other
13253431Scarlsonj 	 * unacceptable configurations.
13260Sstevel@tonic-gate 	 */
1327*4823Sseb 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
1328*4823Sseb 	    IFF_VIRTUAL)) {
13293431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags "
13303431Scarlsonj 		    "are %llx", lif->lif_name, lifr.lifr_flags);
13313431Scarlsonj 		return (B_FALSE);
13320Sstevel@tonic-gate 	}
13330Sstevel@tonic-gate 
13343431Scarlsonj 	if (!(lifr.lifr_flags & IFF_DEPRECATED))
13353431Scarlsonj 		return (B_TRUE);
13360Sstevel@tonic-gate 
13373431Scarlsonj 	lifr.lifr_flags &= ~IFF_DEPRECATED;
13383431Scarlsonj 	if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
13393431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s",
13403431Scarlsonj 		    lif->lif_name);
13413431Scarlsonj 		return (B_FALSE);
13423431Scarlsonj 	} else {
13433431Scarlsonj 		lif->lif_flags = lifr.lifr_flags;
13443431Scarlsonj 		return (B_TRUE);
13450Sstevel@tonic-gate 	}
13460Sstevel@tonic-gate }
13470Sstevel@tonic-gate 
13480Sstevel@tonic-gate /*
13493431Scarlsonj  * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only).
13500Sstevel@tonic-gate  *
13513431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
13523431Scarlsonj  *  output: boolean_t: B_TRUE if the socket was opened successfully.
13530Sstevel@tonic-gate  */
13540Sstevel@tonic-gate 
13553431Scarlsonj boolean_t
13563431Scarlsonj open_ip_lif(dhcp_lif_t *lif)
13570Sstevel@tonic-gate {
13583431Scarlsonj 	if (lif->lif_sock_ip_fd != -1) {
13593431Scarlsonj 		dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s",
13603431Scarlsonj 		    lif->lif_name);
13613431Scarlsonj 		return (B_FALSE);
13620Sstevel@tonic-gate 	}
13630Sstevel@tonic-gate 
13643431Scarlsonj 	lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
13653431Scarlsonj 	if (lif->lif_sock_ip_fd == -1) {
13663431Scarlsonj 		dhcpmsg(MSG_ERR, "open_ip_lif: cannot create v4 socket on %s",
13673431Scarlsonj 		    lif->lif_name);
13683431Scarlsonj 		return (B_FALSE);
13690Sstevel@tonic-gate 	}
13700Sstevel@tonic-gate 
13713431Scarlsonj 	if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC,
13723431Scarlsonj 	    ntohl(lif->lif_addr))) {
13733431Scarlsonj 		dhcpmsg(MSG_ERR, "open_ip_lif: cannot bind v4 socket on %s",
13743431Scarlsonj 		    lif->lif_name);
13753431Scarlsonj 		return (B_FALSE);
13763431Scarlsonj 	}
13770Sstevel@tonic-gate 
13783431Scarlsonj 	lif->lif_acknak_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN,
13793431Scarlsonj 	    dhcp_acknak_lif, lif);
13803431Scarlsonj 	if (lif->lif_acknak_id == -1) {
13813431Scarlsonj 		dhcpmsg(MSG_WARNING, "open_ip_lif: cannot register to "
13823431Scarlsonj 		    "receive IP unicast");
13833431Scarlsonj 		close_ip_lif(lif);
13843431Scarlsonj 		return (B_FALSE);
13850Sstevel@tonic-gate 	}
13860Sstevel@tonic-gate 	return (B_TRUE);
13870Sstevel@tonic-gate }
13880Sstevel@tonic-gate 
13890Sstevel@tonic-gate /*
13903431Scarlsonj  * close_ip_lif(): close an IP socket for I/O on a given LIF.
13910Sstevel@tonic-gate  *
13923431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
13933431Scarlsonj  *  output: none
13940Sstevel@tonic-gate  */
13950Sstevel@tonic-gate 
13963431Scarlsonj void
13973431Scarlsonj close_ip_lif(dhcp_lif_t *lif)
13980Sstevel@tonic-gate {
13993431Scarlsonj 	if (lif->lif_acknak_id != -1) {
14003431Scarlsonj 		(void) iu_unregister_event(eh, lif->lif_acknak_id, NULL);
14013431Scarlsonj 		lif->lif_acknak_id = -1;
14023431Scarlsonj 	}
14033431Scarlsonj 	if (lif->lif_sock_ip_fd != -1) {
14043431Scarlsonj 		(void) close(lif->lif_sock_ip_fd);
14053431Scarlsonj 		lif->lif_sock_ip_fd = -1;
14063431Scarlsonj 	}
14073431Scarlsonj }
14080Sstevel@tonic-gate 
14093431Scarlsonj /*
14103431Scarlsonj  * lif_mark_decline(): mark a LIF as having been declined due to a duplicate
14113431Scarlsonj  *		       address or some other conflict.  This is used in
14123431Scarlsonj  *		       send_declines() to report failure back to the server.
14133431Scarlsonj  *
14143431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
14153431Scarlsonj  *	    const char *: text string explaining why the address is declined
14163431Scarlsonj  *  output: none
14173431Scarlsonj  */
14180Sstevel@tonic-gate 
14193431Scarlsonj void
14203431Scarlsonj lif_mark_decline(dhcp_lif_t *lif, const char *reason)
14213431Scarlsonj {
14223431Scarlsonj 	if (lif->lif_declined == NULL) {
14233431Scarlsonj 		dhcp_lease_t *dlp;
14240Sstevel@tonic-gate 
14253431Scarlsonj 		lif->lif_declined = reason;
14263431Scarlsonj 		if ((dlp = lif->lif_lease) != NULL)
14273431Scarlsonj 			dlp->dl_smach->dsm_lif_down++;
14280Sstevel@tonic-gate 	}
14290Sstevel@tonic-gate }
14300Sstevel@tonic-gate 
14310Sstevel@tonic-gate /*
14323431Scarlsonj  * schedule_lif_timer(): schedules the LIF-related timer
14330Sstevel@tonic-gate  *
14343431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
14353431Scarlsonj  *	    dhcp_timer_t *: the timer to schedule
14363431Scarlsonj  *	    iu_tq_callback_t *: the callback to call upon firing
14373431Scarlsonj  *  output: boolean_t: B_TRUE if the timer was scheduled successfully
14380Sstevel@tonic-gate  */
14390Sstevel@tonic-gate 
14403431Scarlsonj boolean_t
14413431Scarlsonj schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire)
14420Sstevel@tonic-gate {
14430Sstevel@tonic-gate 	/*
14443431Scarlsonj 	 * If there's a timer running, cancel it and release its lease
14453431Scarlsonj 	 * reference.
14460Sstevel@tonic-gate 	 */
14473431Scarlsonj 	if (dt->dt_id != -1) {
14483431Scarlsonj 		if (!cancel_timer(dt))
14493431Scarlsonj 			return (B_FALSE);
14503431Scarlsonj 		release_lif(lif);
14513431Scarlsonj 	}
14520Sstevel@tonic-gate 
14533431Scarlsonj 	if (schedule_timer(dt, expire, lif)) {
14543431Scarlsonj 		hold_lif(lif);
14553431Scarlsonj 		return (B_TRUE);
14563431Scarlsonj 	} else {
14573431Scarlsonj 		dhcpmsg(MSG_WARNING,
14583431Scarlsonj 		    "schedule_lif_timer: cannot schedule timer");
14593431Scarlsonj 		return (B_FALSE);
14603431Scarlsonj 	}
14610Sstevel@tonic-gate }
14620Sstevel@tonic-gate 
14630Sstevel@tonic-gate /*
14643431Scarlsonj  * cancel_lif_timer(): cancels a LIF-related timer
14650Sstevel@tonic-gate  *
14663431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
14673431Scarlsonj  *	    dhcp_timer_t *: the timer to cancel
14683431Scarlsonj  *  output: none
14690Sstevel@tonic-gate  */
14700Sstevel@tonic-gate 
14713431Scarlsonj static void
14723431Scarlsonj cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt)
14730Sstevel@tonic-gate {
14743431Scarlsonj 	if (dt->dt_id == -1)
14753431Scarlsonj 		return;
14763431Scarlsonj 	if (cancel_timer(dt)) {
14773431Scarlsonj 		dhcpmsg(MSG_DEBUG2,
14783431Scarlsonj 		    "cancel_lif_timer: canceled expiry timer on %s",
14793431Scarlsonj 		    lif->lif_name);
14803431Scarlsonj 		release_lif(lif);
14813431Scarlsonj 	} else {
14823431Scarlsonj 		dhcpmsg(MSG_WARNING,
14833431Scarlsonj 		    "cancel_lif_timer: cannot cancel timer on %s",
14843431Scarlsonj 		    lif->lif_name);
14850Sstevel@tonic-gate 	}
14860Sstevel@tonic-gate }
14870Sstevel@tonic-gate 
14880Sstevel@tonic-gate /*
14893431Scarlsonj  * cancel_lif_timers(): cancels the LIF-related timers
14900Sstevel@tonic-gate  *
14913431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
14923431Scarlsonj  *  output: none
14930Sstevel@tonic-gate  */
14940Sstevel@tonic-gate 
14950Sstevel@tonic-gate void
14963431Scarlsonj cancel_lif_timers(dhcp_lif_t *lif)
14970Sstevel@tonic-gate {
14983431Scarlsonj 	cancel_lif_timer(lif, &lif->lif_preferred);
14993431Scarlsonj 	cancel_lif_timer(lif, &lif->lif_expire);
15000Sstevel@tonic-gate }
15010Sstevel@tonic-gate 
15020Sstevel@tonic-gate /*
15033431Scarlsonj  * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common
15043431Scarlsonj  *		  file descriptors (v4_sock_fd and v6_sock_fd).
15050Sstevel@tonic-gate  *
15063431Scarlsonj  *   input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4
15073431Scarlsonj  *  output: none
15080Sstevel@tonic-gate  */
15090Sstevel@tonic-gate 
15103431Scarlsonj uint_t
15113431Scarlsonj get_max_mtu(boolean_t isv6)
15120Sstevel@tonic-gate {
15133431Scarlsonj 	uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu;
15143431Scarlsonj 
15153431Scarlsonj 	if (*mtup == 0) {
15163431Scarlsonj 		dhcp_pif_t *pif;
15173431Scarlsonj 		dhcp_lif_t *lif;
15183431Scarlsonj 		struct lifreq lifr;
15193431Scarlsonj 
15203431Scarlsonj 		/* Set an arbitrary lower bound */
15213431Scarlsonj 		*mtup = 1024;
15223431Scarlsonj 		pif = isv6 ? v6root : v4root;
15233431Scarlsonj 		for (; pif != NULL; pif = pif->pif_next) {
15243431Scarlsonj 			for (lif = pif->pif_lifs; lif != NULL;
15253431Scarlsonj 			    lif = lif->lif_next) {
15263431Scarlsonj 				(void) strlcpy(lifr.lifr_name, lif->lif_name,
15273431Scarlsonj 				    LIFNAMSIZ);
15283431Scarlsonj 				if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) !=
15293431Scarlsonj 				    -1 && lifr.lifr_mtu > *mtup) {
15303431Scarlsonj 					*mtup = lifr.lifr_mtu;
15313431Scarlsonj 				}
15323431Scarlsonj 			}
15333431Scarlsonj 		}
15340Sstevel@tonic-gate 	}
15353431Scarlsonj 	return (*mtup);
15360Sstevel@tonic-gate }
15370Sstevel@tonic-gate 
15380Sstevel@tonic-gate /*
15393431Scarlsonj  * expired_lif_state(): summarize the state of expired LIFs on a given state
15403431Scarlsonj  *			machine.
15410Sstevel@tonic-gate  *
15423431Scarlsonj  *   input: dhcp_smach_t *: the state machine to scan
15433431Scarlsonj  *  output: dhcp_expire_t: overall state
15440Sstevel@tonic-gate  */
15450Sstevel@tonic-gate 
15463431Scarlsonj dhcp_expire_t
15473431Scarlsonj expired_lif_state(dhcp_smach_t *dsmp)
15480Sstevel@tonic-gate {
15493431Scarlsonj 	dhcp_lease_t *dlp;
15503431Scarlsonj 	dhcp_lif_t *lif;
15513431Scarlsonj 	uint_t nlifs;
15523431Scarlsonj 	uint_t numlifs;
15533431Scarlsonj 	uint_t numexp;
15540Sstevel@tonic-gate 
15553431Scarlsonj 	numlifs = numexp = 0;
15563431Scarlsonj 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
15573431Scarlsonj 		lif = dlp->dl_lifs;
15583431Scarlsonj 		nlifs = dlp->dl_nlifs;
15593431Scarlsonj 		numlifs += nlifs;
15603431Scarlsonj 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
15613431Scarlsonj 			if (lif->lif_expired)
15623431Scarlsonj 				numexp++;
15633431Scarlsonj 		}
15640Sstevel@tonic-gate 	}
15653431Scarlsonj 	if (numlifs == 0)
15663431Scarlsonj 		return (DHCP_EXP_NOLIFS);
15673431Scarlsonj 	else if (numexp == 0)
15683431Scarlsonj 		return (DHCP_EXP_NOEXP);
15693431Scarlsonj 	else if (numlifs == numexp)
15703431Scarlsonj 		return (DHCP_EXP_ALLEXP);
15713431Scarlsonj 	else
15723431Scarlsonj 		return (DHCP_EXP_SOMEEXP);
15730Sstevel@tonic-gate }
15740Sstevel@tonic-gate 
15750Sstevel@tonic-gate /*
15763431Scarlsonj  * find_expired_lif(): find the first expired LIF on a given state machine
15770Sstevel@tonic-gate  *
15783431Scarlsonj  *   input: dhcp_smach_t *: the state machine to scan
15793431Scarlsonj  *  output: dhcp_lif_t *: the first expired LIF, or NULL if none.
15800Sstevel@tonic-gate  */
15810Sstevel@tonic-gate 
15823431Scarlsonj dhcp_lif_t *
15833431Scarlsonj find_expired_lif(dhcp_smach_t *dsmp)
15840Sstevel@tonic-gate {
15853431Scarlsonj 	dhcp_lease_t *dlp;
15863431Scarlsonj 	dhcp_lif_t *lif;
15873431Scarlsonj 	uint_t nlifs;
15883431Scarlsonj 
15893431Scarlsonj 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
15903431Scarlsonj 		lif = dlp->dl_lifs;
15913431Scarlsonj 		nlifs = dlp->dl_nlifs;
15923431Scarlsonj 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
15933431Scarlsonj 			if (lif->lif_expired)
15943431Scarlsonj 				return (lif);
15953431Scarlsonj 		}
15963431Scarlsonj 	}
15973431Scarlsonj 	return (NULL);
15983431Scarlsonj }
15993431Scarlsonj 
16003431Scarlsonj /*
16013431Scarlsonj  * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING.  Used
16023431Scarlsonj  *		       only for DHCPv6.
16033431Scarlsonj  *
16043431Scarlsonj  *   input: none
16053431Scarlsonj  *  output: none
16063431Scarlsonj  */
16073431Scarlsonj 
16083431Scarlsonj void
16093431Scarlsonj remove_v6_strays(void)
16103431Scarlsonj {
16113431Scarlsonj 	struct lifnum lifn;
16123431Scarlsonj 	struct lifconf lifc;
16133431Scarlsonj 	struct lifreq *lifrp, *lifrmax;
16143431Scarlsonj 	uint_t numifs;
16153431Scarlsonj 	uint64_t flags;
16160Sstevel@tonic-gate 
16170Sstevel@tonic-gate 	/*
16183431Scarlsonj 	 * Get the approximate number of interfaces in the system.  It's only
16193431Scarlsonj 	 * approximate because the system is dynamic -- interfaces may be
16203431Scarlsonj 	 * plumbed or unplumbed at any time.  This is also the reason for the
16213431Scarlsonj 	 * "+ 10" fudge factor: we're trying to avoid unnecessary looping.
16220Sstevel@tonic-gate 	 */
16233431Scarlsonj 	(void) memset(&lifn, 0, sizeof (lifn));
16243431Scarlsonj 	lifn.lifn_family = AF_INET6;
16253431Scarlsonj 	lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
16263431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) {
16273431Scarlsonj 		dhcpmsg(MSG_ERR,
16283431Scarlsonj 		    "remove_v6_strays: cannot read number of interfaces");
16293431Scarlsonj 		numifs = 10;
16303431Scarlsonj 	} else {
16313431Scarlsonj 		numifs = lifn.lifn_count + 10;
16320Sstevel@tonic-gate 	}
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate 	/*
16353431Scarlsonj 	 * Get the interface information.  We do this in a loop so that we can
16363431Scarlsonj 	 * recover from EINVAL from the kernel -- delivered when the buffer is
16373431Scarlsonj 	 * too small.
16380Sstevel@tonic-gate 	 */
16393431Scarlsonj 	(void) memset(&lifc, 0, sizeof (lifc));
16403431Scarlsonj 	lifc.lifc_family = AF_INET6;
16413431Scarlsonj 	lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
16423431Scarlsonj 	for (;;) {
16433431Scarlsonj 		lifc.lifc_len = numifs * sizeof (*lifrp);
16443431Scarlsonj 		lifrp = realloc(lifc.lifc_buf, lifc.lifc_len);
16453431Scarlsonj 		if (lifrp == NULL) {
16463431Scarlsonj 			dhcpmsg(MSG_ERR,
16473431Scarlsonj 			    "remove_v6_strays: cannot allocate memory");
16483431Scarlsonj 			free(lifc.lifc_buf);
16493431Scarlsonj 			return;
16500Sstevel@tonic-gate 		}
16513431Scarlsonj 		lifc.lifc_buf = (caddr_t)lifrp;
16523431Scarlsonj 		errno = 0;
16533431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 &&
16543431Scarlsonj 		    lifc.lifc_len < numifs * sizeof (*lifrp))
16550Sstevel@tonic-gate 			break;
16563431Scarlsonj 		if (errno == 0 || errno == EINVAL) {
16573431Scarlsonj 			numifs <<= 1;
16583431Scarlsonj 		} else {
16593431Scarlsonj 			dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF");
16603431Scarlsonj 			free(lifc.lifc_buf);
16613431Scarlsonj 			return;
16620Sstevel@tonic-gate 		}
16630Sstevel@tonic-gate 	}
16640Sstevel@tonic-gate 
16653431Scarlsonj 	lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp);
16663431Scarlsonj 	for (; lifrp < lifrmax; lifrp++) {
16670Sstevel@tonic-gate 		/*
16683431Scarlsonj 		 * Get the interface flags; we're interested in the DHCP ones.
16690Sstevel@tonic-gate 		 */
16703431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1)
16713431Scarlsonj 			continue;
16723431Scarlsonj 		flags = lifrp->lifr_flags;
16733431Scarlsonj 		if (!(flags & IFF_DHCPRUNNING))
16743431Scarlsonj 			continue;
16750Sstevel@tonic-gate 		/*
16763431Scarlsonj 		 * If the interface has a link-local address, then we don't
16773431Scarlsonj 		 * control it.  Just remove the flag.
16780Sstevel@tonic-gate 		 */
16793431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1)
16803431Scarlsonj 			continue;
16813431Scarlsonj 		if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp->
16823431Scarlsonj 		    lifr_addr)->sin6_addr)) {
16833431Scarlsonj 			lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING;
16843431Scarlsonj 			(void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp);
16853431Scarlsonj 			continue;
16860Sstevel@tonic-gate 		}
16870Sstevel@tonic-gate 		/*
16883431Scarlsonj 		 * All others are (or were) under our control.  Clean up by
16893431Scarlsonj 		 * removing them.
16900Sstevel@tonic-gate 		 */
16913431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) {
16923431Scarlsonj 			dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s",
16933431Scarlsonj 			    lifrp->lifr_name);
16943431Scarlsonj 		} else if (errno != ENXIO) {
16953431Scarlsonj 			dhcpmsg(MSG_ERR,
16963431Scarlsonj 			    "remove_v6_strays: SIOCLIFREMOVEIF %s",
16973431Scarlsonj 			    lifrp->lifr_name);
16980Sstevel@tonic-gate 		}
16990Sstevel@tonic-gate 	}
17003431Scarlsonj 	free(lifc.lifc_buf);
17010Sstevel@tonic-gate }
1702