xref: /onnv-gate/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c (revision 11076:445f05f9f7b4)
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 /*
228485SPeter.Memishian@Sun.COM  * Copyright 2009 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>
410Sstevel@tonic-gate 
423431Scarlsonj #include "agent.h"
430Sstevel@tonic-gate #include "interface.h"
440Sstevel@tonic-gate #include "util.h"
450Sstevel@tonic-gate #include "packet.h"
460Sstevel@tonic-gate #include "states.h"
473431Scarlsonj 
483431Scarlsonj dhcp_pif_t *v4root;
493431Scarlsonj dhcp_pif_t *v6root;
503431Scarlsonj 
513431Scarlsonj static uint_t cached_v4_max_mtu, cached_v6_max_mtu;
520Sstevel@tonic-gate 
530Sstevel@tonic-gate /*
543431Scarlsonj  * Interface flags to watch: things that should be under our direct control.
550Sstevel@tonic-gate  */
564823Sseb #define	DHCP_IFF_WATCH	(IFF_DHCPRUNNING | IFF_DEPRECATED | IFF_ADDRCONF | \
574823Sseb 	IFF_TEMPORARY)
580Sstevel@tonic-gate 
593431Scarlsonj static void clear_lif_dhcp(dhcp_lif_t *);
600Sstevel@tonic-gate 
610Sstevel@tonic-gate /*
623431Scarlsonj  * insert_pif(): creates a new physical interface structure and chains it on
633431Scarlsonj  *		 the list.  Initializes state that remains consistent across
643431Scarlsonj  *		 all use of the physical interface entry.
650Sstevel@tonic-gate  *
663431Scarlsonj  *   input: const char *: the name of the physical interface
673431Scarlsonj  *	    boolean_t: if B_TRUE, this is DHCPv6
683431Scarlsonj  *	    int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
690Sstevel@tonic-gate  *		   error code with the reason why
703431Scarlsonj  *  output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure
710Sstevel@tonic-gate  */
720Sstevel@tonic-gate 
733431Scarlsonj dhcp_pif_t *
insert_pif(const char * pname,boolean_t isv6,int * error)743431Scarlsonj insert_pif(const char *pname, boolean_t isv6, int *error)
750Sstevel@tonic-gate {
763431Scarlsonj 	dhcp_pif_t *pif;
773431Scarlsonj 	struct lifreq lifr;
788485SPeter.Memishian@Sun.COM 	lifgroupinfo_t lifgr;
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 
114*11076SCathy.Zhou@Sun.COM 	/*
115*11076SCathy.Zhou@Sun.COM 	 * Check if this is a VRRP interface. If yes, its IP addresses (the
116*11076SCathy.Zhou@Sun.COM 	 * VRRP virtual addresses) cannot be configured using DHCP.
117*11076SCathy.Zhou@Sun.COM 	 */
118*11076SCathy.Zhou@Sun.COM 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
119*11076SCathy.Zhou@Sun.COM 		*error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
120*11076SCathy.Zhou@Sun.COM 		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFFLAGS for %s", pname);
121*11076SCathy.Zhou@Sun.COM 		goto failure;
122*11076SCathy.Zhou@Sun.COM 	}
123*11076SCathy.Zhou@Sun.COM 
124*11076SCathy.Zhou@Sun.COM 	if (lifr.lifr_flags & IFF_VRRP) {
125*11076SCathy.Zhou@Sun.COM 		*error = DHCP_IPC_E_INVIF;
126*11076SCathy.Zhou@Sun.COM 		dhcpmsg(MSG_ERROR, "insert_pif: VRRP virtual addresses over %s "
127*11076SCathy.Zhou@Sun.COM 		    "cannot be configured using DHCP", pname);
128*11076SCathy.Zhou@Sun.COM 		goto failure;
129*11076SCathy.Zhou@Sun.COM 	}
130*11076SCathy.Zhou@Sun.COM 
1315978Smeem 	if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1) {
1325978Smeem 		*error = (errno == ENXIO) ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT;
1335978Smeem 		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFMTU for %s", pname);
1345978Smeem 		goto failure;
1355978Smeem 	}
1365978Smeem 	pif->pif_max = lifr.lifr_mtu;
1373431Scarlsonj 
1385978Smeem 	if (pif->pif_max < DHCP_DEF_MAX_SIZE) {
1395978Smeem 		dhcpmsg(MSG_ERROR, "insert_pif: MTU of %s is too small to "
1405978Smeem 		    "support DHCP (%u < %u)", pname, pif->pif_max,
1415978Smeem 		    DHCP_DEF_MAX_SIZE);
1425978Smeem 		*error = DHCP_IPC_E_INVIF;
1435978Smeem 		goto failure;
1445978Smeem 	}
1455978Smeem 
1465978Smeem 	/*
1478485SPeter.Memishian@Sun.COM 	 * Check if the pif is in an IPMP group.  Interfaces using IPMP don't
1488485SPeter.Memishian@Sun.COM 	 * have dedicated hardware addresses, and get their hardware type from
1498485SPeter.Memishian@Sun.COM 	 * the SIOCGLIFGROUPINFO ioctl rather than DLPI.
1505978Smeem 	 */
1518485SPeter.Memishian@Sun.COM 	if (ioctl(fd, SIOCGLIFGROUPNAME, &lifr) == -1) {
1528485SPeter.Memishian@Sun.COM 		*error = DHCP_IPC_E_INT;
1538485SPeter.Memishian@Sun.COM 		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFGROUPNAME for %s", pname);
1548485SPeter.Memishian@Sun.COM 		goto failure;
1558485SPeter.Memishian@Sun.COM 	}
1568485SPeter.Memishian@Sun.COM 
1578485SPeter.Memishian@Sun.COM 	if (lifr.lifr_groupname[0] != '\0') {
1588485SPeter.Memishian@Sun.COM 		(void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname,
1598485SPeter.Memishian@Sun.COM 		    LIFGRNAMSIZ);
1608485SPeter.Memishian@Sun.COM 		if (ioctl(fd, SIOCGLIFGROUPINFO, &lifgr) == -1) {
1618485SPeter.Memishian@Sun.COM 			*error = DHCP_IPC_E_INT;
1628485SPeter.Memishian@Sun.COM 			dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFGROUPINFO for %s",
1638485SPeter.Memishian@Sun.COM 			    lifgr.gi_grname);
1648485SPeter.Memishian@Sun.COM 			goto failure;
1658485SPeter.Memishian@Sun.COM 		}
1668485SPeter.Memishian@Sun.COM 
1678485SPeter.Memishian@Sun.COM 		pif->pif_hwtype = dlpi_arptype(lifgr.gi_mactype);
1688485SPeter.Memishian@Sun.COM 		pif->pif_under_ipmp = (strcmp(pname, lifgr.gi_grifname) != 0);
1698485SPeter.Memishian@Sun.COM 		(void) strlcpy(pif->pif_grifname, lifgr.gi_grifname, LIFNAMSIZ);
1708485SPeter.Memishian@Sun.COM 
1718485SPeter.Memishian@Sun.COM 		/*
1728485SPeter.Memishian@Sun.COM 		 * For IPMP underlying interfaces, stash the interface index
1738485SPeter.Memishian@Sun.COM 		 * of the IPMP meta-interface; we'll use it to send/receive
1748485SPeter.Memishian@Sun.COM 		 * traffic.  This is both necessary (since IP_BOUND_IF for
1758485SPeter.Memishian@Sun.COM 		 * non-unicast traffic won't work on underlying interfaces)
1768485SPeter.Memishian@Sun.COM 		 * and preferred (since a test address lease will be able to
1778485SPeter.Memishian@Sun.COM 		 * be maintained as long as another interface in the group is
1788485SPeter.Memishian@Sun.COM 		 * still functioning).
1798485SPeter.Memishian@Sun.COM 		 */
1808485SPeter.Memishian@Sun.COM 		if (pif->pif_under_ipmp) {
1818485SPeter.Memishian@Sun.COM 			(void) strlcpy(lifr.lifr_name, pif->pif_grifname,
1828485SPeter.Memishian@Sun.COM 			    LIFNAMSIZ);
1838485SPeter.Memishian@Sun.COM 
1848485SPeter.Memishian@Sun.COM 			if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
1858485SPeter.Memishian@Sun.COM 				*error = DHCP_IPC_E_INT;
1868485SPeter.Memishian@Sun.COM 				dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX "
1878485SPeter.Memishian@Sun.COM 				    "for %s", lifr.lifr_name);
1888485SPeter.Memishian@Sun.COM 				goto failure;
1898485SPeter.Memishian@Sun.COM 			}
1908485SPeter.Memishian@Sun.COM 			pif->pif_grindex = lifr.lifr_index;
1918485SPeter.Memishian@Sun.COM 		}
1928485SPeter.Memishian@Sun.COM 	}
1938485SPeter.Memishian@Sun.COM 
1948485SPeter.Memishian@Sun.COM 	/*
1958485SPeter.Memishian@Sun.COM 	 * For IPv4, if the hardware type is still unknown, use DLPI to
1968485SPeter.Memishian@Sun.COM 	 * determine it, the hardware address, and hardware address length.
1978485SPeter.Memishian@Sun.COM 	 */
1988485SPeter.Memishian@Sun.COM 	if (!isv6 && pif->pif_hwtype == 0) {
1998485SPeter.Memishian@Sun.COM 		int			rc;
2008485SPeter.Memishian@Sun.COM 		dlpi_info_t		dlinfo;
2015978Smeem 
2024456Sss150715 		if ((rc = dlpi_open(pname, &dh, 0)) != DLPI_SUCCESS) {
2034456Sss150715 			dhcpmsg(MSG_ERROR, "insert_pif: dlpi_open: %s",
2044456Sss150715 			    dlpi_strerror(rc));
2054456Sss150715 			*error = DHCP_IPC_E_INVIF;
2064456Sss150715 			goto failure;
2074456Sss150715 		}
2084456Sss150715 
2094456Sss150715 		if ((rc = dlpi_bind(dh, ETHERTYPE_IP, NULL)) != DLPI_SUCCESS) {
2104456Sss150715 			dhcpmsg(MSG_ERROR, "insert_pif: dlpi_bind: %s",
2114456Sss150715 			    dlpi_strerror(rc));
2123431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
2133431Scarlsonj 			goto failure;
2143431Scarlsonj 		}
2153431Scarlsonj 
2165978Smeem 		if ((rc = dlpi_info(dh, &dlinfo, 0)) != DLPI_SUCCESS) {
2174456Sss150715 			dhcpmsg(MSG_ERROR, "insert_pif: dlpi_info: %s",
2184456Sss150715 			    dlpi_strerror(rc));
2194456Sss150715 			*error = DHCP_IPC_E_INVIF;
2204456Sss150715 			goto failure;
2214456Sss150715 		}
2224456Sss150715 
2234456Sss150715 		pif->pif_hwtype = dlpi_arptype(dlinfo.di_mactype);
2244456Sss150715 		pif->pif_hwlen  = dlinfo.di_physaddrlen;
2253431Scarlsonj 
2265381Smeem 		dhcpmsg(MSG_DEBUG, "insert_pif: %s: hwtype %d, hwlen %d",
2275381Smeem 		    pname, pif->pif_hwtype, pif->pif_hwlen);
2283431Scarlsonj 
2293431Scarlsonj 		if (pif->pif_hwlen > 0) {
2303431Scarlsonj 			pif->pif_hwaddr = malloc(pif->pif_hwlen);
2313431Scarlsonj 			if (pif->pif_hwaddr == NULL) {
2323431Scarlsonj 				dhcpmsg(MSG_ERR, "insert_pif: cannot allocate "
2333431Scarlsonj 				    "pif_hwaddr for %s", pname);
2343431Scarlsonj 				*error = DHCP_IPC_E_MEMORY;
2353431Scarlsonj 				goto failure;
2363431Scarlsonj 			}
2375381Smeem 			(void) memcpy(pif->pif_hwaddr, dlinfo.di_physaddr,
2385381Smeem 			    pif->pif_hwlen);
2393431Scarlsonj 		}
2403431Scarlsonj 
2415381Smeem 		dlpi_close(dh);
2425381Smeem 		dh = NULL;
2433431Scarlsonj 	}
2443431Scarlsonj 
2453431Scarlsonj 	insque(pif, isv6 ? &v6root : &v4root);
2463431Scarlsonj 
2473431Scarlsonj 	return (pif);
2483431Scarlsonj failure:
2495381Smeem 	if (dh != NULL)
2505381Smeem 		dlpi_close(dh);
2513431Scarlsonj 	release_pif(pif);
2523431Scarlsonj 	return (NULL);
2533431Scarlsonj }
2543431Scarlsonj 
2553431Scarlsonj /*
2563431Scarlsonj  * hold_pif(): acquire a hold on a physical interface structure.
2573431Scarlsonj  *
2583431Scarlsonj  *   input: dhcp_pif_t *: a pointer to the PIF structure
2593431Scarlsonj  *  output: none
2603431Scarlsonj  */
2613431Scarlsonj 
2623431Scarlsonj void
hold_pif(dhcp_pif_t * pif)2633431Scarlsonj hold_pif(dhcp_pif_t *pif)
2643431Scarlsonj {
2653431Scarlsonj 	pif->pif_hold_count++;
2663431Scarlsonj 	dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name,
2673431Scarlsonj 	    pif->pif_hold_count);
2683431Scarlsonj }
2693431Scarlsonj 
2703431Scarlsonj /*
2713431Scarlsonj  * release_pif(): release a hold on a physical interface structure; will
2723431Scarlsonj  *		  destroy the structure on the last hold removed.
2733431Scarlsonj  *
2743431Scarlsonj  *   input: dhcp_pif_t *: a pointer to the PIF structure
2753431Scarlsonj  *  output: none
2763431Scarlsonj  */
2773431Scarlsonj 
2783431Scarlsonj void
release_pif(dhcp_pif_t * pif)2793431Scarlsonj release_pif(dhcp_pif_t *pif)
2803431Scarlsonj {
2813431Scarlsonj 	if (pif->pif_hold_count == 0) {
2823431Scarlsonj 		dhcpmsg(MSG_CRIT, "release_pif: extraneous release");
2833431Scarlsonj 		return;
2843431Scarlsonj 	}
2853431Scarlsonj 
2863431Scarlsonj 	if (--pif->pif_hold_count == 0) {
2873431Scarlsonj 		dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s",
2883431Scarlsonj 		    pif->pif_name);
2893431Scarlsonj 
2903431Scarlsonj 		remque(pif);
2913431Scarlsonj 		free(pif->pif_hwaddr);
2923431Scarlsonj 		free(pif);
2933431Scarlsonj 	} else {
2943431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u",
2953431Scarlsonj 		    pif->pif_name, pif->pif_hold_count);
2963431Scarlsonj 	}
2973431Scarlsonj }
2983431Scarlsonj 
2993431Scarlsonj /*
3003431Scarlsonj  * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and
3013431Scarlsonj  *			   previous PIF pointer (or NULL for list start).
3023431Scarlsonj  *			   Caller is expected to iterate through all
3033431Scarlsonj  *			   potential matches to find interface of interest.
3043431Scarlsonj  *
3053431Scarlsonj  *   input: uint16_t: the interface index (truncated)
3063431Scarlsonj  *	    dhcp_pif_t *: the previous PIF, or NULL for list start
3073431Scarlsonj  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
3083431Scarlsonj  *  output: dhcp_pif_t *: the next matching PIF, or NULL if not found
3093431Scarlsonj  *    note: This operates using the 'truncated' (16-bit) ifindex as seen by
3103431Scarlsonj  *	    routing socket clients.  The value stored in pif_index is the
3113431Scarlsonj  *	    32-bit ifindex from the ioctl interface.
3123431Scarlsonj  */
3133431Scarlsonj 
3143431Scarlsonj dhcp_pif_t *
lookup_pif_by_uindex(uint16_t ifindex,dhcp_pif_t * pif,boolean_t isv6)3153431Scarlsonj lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6)
3163431Scarlsonj {
3173431Scarlsonj 	if (pif == NULL)
3183431Scarlsonj 		pif = isv6 ? v6root : v4root;
3193431Scarlsonj 	else
3203431Scarlsonj 		pif = pif->pif_next;
3213431Scarlsonj 
3223431Scarlsonj 	for (; pif != NULL; pif = pif->pif_next) {
3233431Scarlsonj 		if ((pif->pif_index & 0xffff) == ifindex)
3243431Scarlsonj 			break;
3253431Scarlsonj 	}
3263431Scarlsonj 
3273431Scarlsonj 	return (pif);
3283431Scarlsonj }
3293431Scarlsonj 
3303431Scarlsonj /*
3313431Scarlsonj  * lookup_pif_by_name(): Looks up a physical interface entry given a name.
3323431Scarlsonj  *
3333431Scarlsonj  *   input: const char *: the physical interface name
3343431Scarlsonj  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
3353431Scarlsonj  *  output: dhcp_pif_t *: the matching PIF, or NULL if not found
3363431Scarlsonj  */
3373431Scarlsonj 
3383431Scarlsonj dhcp_pif_t *
lookup_pif_by_name(const char * pname,boolean_t isv6)3393431Scarlsonj lookup_pif_by_name(const char *pname, boolean_t isv6)
3403431Scarlsonj {
3413431Scarlsonj 	dhcp_pif_t *pif;
3423431Scarlsonj 
3433431Scarlsonj 	pif = isv6 ? v6root : v4root;
3443431Scarlsonj 
3453431Scarlsonj 	for (; pif != NULL; pif = pif->pif_next) {
3463431Scarlsonj 		if (strcmp(pif->pif_name, pname) == 0)
3473431Scarlsonj 			break;
3483431Scarlsonj 	}
3493431Scarlsonj 
3503431Scarlsonj 	return (pif);
3513431Scarlsonj }
3523431Scarlsonj 
3533431Scarlsonj /*
3543431Scarlsonj  * pif_status(): update the physical interface up/down status.
3553431Scarlsonj  *
3565381Smeem  *   input: dhcp_pif_t *: the physical interface to be updated
3573431Scarlsonj  *	    boolean_t: B_TRUE if the interface is going up
3583431Scarlsonj  *  output: none
3593431Scarlsonj  */
3603431Scarlsonj 
3613431Scarlsonj void
pif_status(dhcp_pif_t * pif,boolean_t isup)3623431Scarlsonj pif_status(dhcp_pif_t *pif, boolean_t isup)
3633431Scarlsonj {
3643431Scarlsonj 	dhcp_lif_t *lif;
3653431Scarlsonj 	dhcp_smach_t *dsmp;
3663431Scarlsonj 
3673431Scarlsonj 	pif->pif_running = isup;
3684516Scarlsonj 	dhcpmsg(MSG_DEBUG, "interface %s has %s", pif->pif_name,
3693431Scarlsonj 	    isup ? "come back up" : "gone down");
3703431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
3713431Scarlsonj 		for (dsmp = lif->lif_smachs; dsmp != NULL;
3723431Scarlsonj 		    dsmp = dsmp->dsm_next) {
3733431Scarlsonj 			if (isup)
3743431Scarlsonj 				refresh_smach(dsmp);
3753431Scarlsonj 			else
3763431Scarlsonj 				remove_default_routes(dsmp);
3773431Scarlsonj 		}
3783431Scarlsonj 	}
3793431Scarlsonj }
3803431Scarlsonj 
3813431Scarlsonj /* Helper for insert_lif: extract addresses as defined */
3823431Scarlsonj #define	ASSIGN_ADDR(v4, v6, lf) \
3833431Scarlsonj 	if (pif->pif_isv6) { \
3843431Scarlsonj 		lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \
3853431Scarlsonj 	} else { \
3863431Scarlsonj 		lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \
3873431Scarlsonj 	}
3883431Scarlsonj 
3893431Scarlsonj /*
3903431Scarlsonj  * insert_lif(): Creates a new logical interface structure and chains it on
3913431Scarlsonj  *		 the list for a given physical interface.  Initializes state
3923431Scarlsonj  *		 that remains consistent across all use of the logical
3933431Scarlsonj  *		 interface entry.  Caller's PIF hold is transferred to the
3943431Scarlsonj  *		 LIF on success, and is dropped on failure.
3953431Scarlsonj  *
3963431Scarlsonj  *   input: dhcp_pif_t *: pointer to the physical interface for this LIF
3973431Scarlsonj  *	    const char *: the name of the logical interface
3983431Scarlsonj  *	    int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
3993431Scarlsonj  *		   error code with the reason why
4003431Scarlsonj  *  output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure
4013431Scarlsonj  */
4023431Scarlsonj 
4033431Scarlsonj dhcp_lif_t *
insert_lif(dhcp_pif_t * pif,const char * lname,int * error)4043431Scarlsonj insert_lif(dhcp_pif_t *pif, const char *lname, int *error)
4053431Scarlsonj {
4063431Scarlsonj 	dhcp_lif_t *lif;
4073431Scarlsonj 	int fd;
4083431Scarlsonj 	struct lifreq lifr;
4093431Scarlsonj 
4103431Scarlsonj 	if ((lif = calloc(1, sizeof (*lif))) == NULL) {
4113431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for "
4123431Scarlsonj 		    "%s", lname);
4130Sstevel@tonic-gate 		*error = DHCP_IPC_E_MEMORY;
4140Sstevel@tonic-gate 		return (NULL);
4150Sstevel@tonic-gate 	}
4160Sstevel@tonic-gate 
4173431Scarlsonj 	lif->lif_sock_ip_fd = -1;
4185381Smeem 	lif->lif_packet_id = -1;
4193431Scarlsonj 	lif->lif_iaid_id = -1;
4203431Scarlsonj 	lif->lif_hold_count = 1;
4213431Scarlsonj 	lif->lif_pif = pif;
4223431Scarlsonj 	lif->lif_removed = B_TRUE;
4233431Scarlsonj 	init_timer(&lif->lif_preferred, 0);
4243431Scarlsonj 	init_timer(&lif->lif_expire, 0);
4250Sstevel@tonic-gate 
4263431Scarlsonj 	if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) {
4273431Scarlsonj 		dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long",
4283431Scarlsonj 		    lname);
4290Sstevel@tonic-gate 		*error = DHCP_IPC_E_INVIF;
4300Sstevel@tonic-gate 		goto failure;
4310Sstevel@tonic-gate 	}
4320Sstevel@tonic-gate 
4333431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ);
4343431Scarlsonj 
4353431Scarlsonj 	fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
4360Sstevel@tonic-gate 
4373431Scarlsonj 	if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1)
4383431Scarlsonj 		lif->lif_max = 1024;
4393431Scarlsonj 	else
4403431Scarlsonj 		lif->lif_max = lifr.lifr_mtu;
4410Sstevel@tonic-gate 
4423431Scarlsonj 	if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) {
4433431Scarlsonj 		if (errno == ENXIO)
4443431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
4453431Scarlsonj 		else
4463431Scarlsonj 			*error = DHCP_IPC_E_INT;
4473431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname);
4480Sstevel@tonic-gate 		goto failure;
4490Sstevel@tonic-gate 	}
4503431Scarlsonj 	ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr);
4510Sstevel@tonic-gate 
4523431Scarlsonj 	if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) {
4532546Scarlsonj 		if (errno == ENXIO)
4542546Scarlsonj 			*error = DHCP_IPC_E_INVIF;
4552546Scarlsonj 		else
4562546Scarlsonj 			*error = DHCP_IPC_E_INT;
4573431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname);
4583431Scarlsonj 		goto failure;
4593431Scarlsonj 	}
4603431Scarlsonj 	ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr);
4613431Scarlsonj 
4623431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
4633431Scarlsonj 		*error = DHCP_IPC_E_INT;
4643431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname);
4652546Scarlsonj 		goto failure;
4662546Scarlsonj 	}
4673431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
4683431Scarlsonj 
4693431Scarlsonj 	/*
4703431Scarlsonj 	 * If we've just detected the interface going up or down, then signal
4713431Scarlsonj 	 * an appropriate action.  There may be other state machines here.
4723431Scarlsonj 	 */
4733431Scarlsonj 	if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) {
4743431Scarlsonj 		pif_status(pif, B_TRUE);
4753431Scarlsonj 	} else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) {
4763431Scarlsonj 		pif_status(pif, B_FALSE);
4773431Scarlsonj 	}
4783431Scarlsonj 
4793431Scarlsonj 	if (lifr.lifr_flags & IFF_POINTOPOINT) {
4803431Scarlsonj 		if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) {
4813431Scarlsonj 			*error = DHCP_IPC_E_INT;
4823431Scarlsonj 			dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s",
4833431Scarlsonj 			    lname);
4843431Scarlsonj 			goto failure;
4853431Scarlsonj 		}
4863431Scarlsonj 		ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr);
4873431Scarlsonj 	} else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) {
4883431Scarlsonj 		if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) {
4893431Scarlsonj 			*error = DHCP_IPC_E_INT;
4903431Scarlsonj 			dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s",
4913431Scarlsonj 			    lname);
4923431Scarlsonj 			goto failure;
4933431Scarlsonj 		}
4943431Scarlsonj 		lif->lif_broadcast =
4953431Scarlsonj 		    ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr.
4963431Scarlsonj 		    s_addr;
4973431Scarlsonj 	}
4983431Scarlsonj 
4993431Scarlsonj 	if (pif->pif_isv6)
5003431Scarlsonj 		cached_v6_max_mtu = 0;
5013431Scarlsonj 	else
5023431Scarlsonj 		cached_v4_max_mtu = 0;
5033431Scarlsonj 
5043431Scarlsonj 	lif->lif_removed = B_FALSE;
5053431Scarlsonj 	insque(lif, &pif->pif_lifs);
5063431Scarlsonj 
5073431Scarlsonj 	return (lif);
5083431Scarlsonj 
5093431Scarlsonj failure:
5103431Scarlsonj 	release_lif(lif);
5113431Scarlsonj 	return (NULL);
5123431Scarlsonj }
5133431Scarlsonj 
5143431Scarlsonj /*
5153431Scarlsonj  * hold_lif(): acquire a hold on a logical interface structure.
5163431Scarlsonj  *
5173431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
5183431Scarlsonj  *  output: none
5193431Scarlsonj  */
5203431Scarlsonj 
5213431Scarlsonj void
hold_lif(dhcp_lif_t * lif)5223431Scarlsonj hold_lif(dhcp_lif_t *lif)
5233431Scarlsonj {
5243431Scarlsonj 	lif->lif_hold_count++;
5253431Scarlsonj 	dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name,
5263431Scarlsonj 	    lif->lif_hold_count);
5273431Scarlsonj }
5283431Scarlsonj 
5293431Scarlsonj /*
5303431Scarlsonj  * release_lif(): release a hold on a logical interface structure; will
5313431Scarlsonj  *		  destroy the structure on the last hold removed.
5323431Scarlsonj  *
5333431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
5343431Scarlsonj  *  output: none
5353431Scarlsonj  */
5363431Scarlsonj 
5373431Scarlsonj void
release_lif(dhcp_lif_t * lif)5383431Scarlsonj release_lif(dhcp_lif_t *lif)
5393431Scarlsonj {
5403431Scarlsonj 	if (lif->lif_hold_count == 0) {
5413431Scarlsonj 		dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s",
5423431Scarlsonj 		    lif->lif_name);
5433431Scarlsonj 		return;
5443431Scarlsonj 	}
5453431Scarlsonj 
5463431Scarlsonj 	if (lif->lif_hold_count == 1 && !lif->lif_removed) {
5473431Scarlsonj 		unplumb_lif(lif);
5483431Scarlsonj 		return;
5493431Scarlsonj 	}
5503431Scarlsonj 
5513431Scarlsonj 	if (--lif->lif_hold_count == 0) {
5523431Scarlsonj 		dhcp_pif_t *pif;
5533431Scarlsonj 
5543431Scarlsonj 		dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s",
5553431Scarlsonj 		    lif->lif_name);
5563431Scarlsonj 
5573431Scarlsonj 		if (lif->lif_lease != NULL)
5583431Scarlsonj 			dhcpmsg(MSG_CRIT,
5593431Scarlsonj 			    "release_lif: still holding lease at last hold!");
5603431Scarlsonj 		close_ip_lif(lif);
5613431Scarlsonj 		pif = lif->lif_pif;
5623431Scarlsonj 		if (pif->pif_isv6)
5633431Scarlsonj 			cached_v6_max_mtu = 0;
5643431Scarlsonj 		else
5653431Scarlsonj 			cached_v4_max_mtu = 0;
5663431Scarlsonj 		release_pif(pif);
5673431Scarlsonj 		free(lif);
5683431Scarlsonj 	} else {
5693431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u",
5703431Scarlsonj 		    lif->lif_name, lif->lif_hold_count);
5713431Scarlsonj 	}
5723431Scarlsonj }
5733431Scarlsonj 
5743431Scarlsonj /*
5753431Scarlsonj  * remove_lif(): remove a logical interface from its PIF and lease (if any) and
5763431Scarlsonj  *		 the lease's hold on the LIF.  Assumes that we did not plumb
5773431Scarlsonj  *		 the interface.
5783431Scarlsonj  *
5793431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
5803431Scarlsonj  *  output: none
5813431Scarlsonj  */
5823431Scarlsonj 
5833431Scarlsonj void
remove_lif(dhcp_lif_t * lif)5843431Scarlsonj remove_lif(dhcp_lif_t *lif)
5853431Scarlsonj {
5863431Scarlsonj 	if (lif->lif_plumbed) {
5873431Scarlsonj 		dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s",
5883431Scarlsonj 		    lif->lif_name);
5893431Scarlsonj 		return;
5903431Scarlsonj 	}
5913431Scarlsonj 	if (lif->lif_removed) {
5923431Scarlsonj 		dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s",
5933431Scarlsonj 		    lif->lif_name);
5943431Scarlsonj 	} else {
5953431Scarlsonj 		dhcp_lif_t *lifnext;
5963431Scarlsonj 		dhcp_lease_t *dlp;
5973431Scarlsonj 
5983431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name);
5993431Scarlsonj 		lif->lif_removed = B_TRUE;
6003431Scarlsonj 		lifnext = lif->lif_next;
6013431Scarlsonj 		clear_lif_dhcp(lif);
6023431Scarlsonj 		cancel_lif_timers(lif);
6033431Scarlsonj 		if (lif->lif_iaid_id != -1 &&
6043431Scarlsonj 		    iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) {
6053431Scarlsonj 			lif->lif_iaid_id = -1;
6063431Scarlsonj 			release_lif(lif);
6073431Scarlsonj 		}
6083431Scarlsonj 
6093431Scarlsonj 		/* Remove from PIF list */
6103431Scarlsonj 		remque(lif);
6113431Scarlsonj 
6123431Scarlsonj 		/* If we were part of a lease, then remove ourselves */
6133431Scarlsonj 		if ((dlp = lif->lif_lease) != NULL) {
6143431Scarlsonj 			if (--dlp->dl_nlifs == 0)
6153431Scarlsonj 				dlp->dl_lifs = NULL;
6163431Scarlsonj 			else if (dlp->dl_lifs == lif)
6173431Scarlsonj 				dlp->dl_lifs = lifnext;
6183431Scarlsonj 			if (lif->lif_declined != NULL) {
6193431Scarlsonj 				dlp->dl_smach->dsm_lif_down--;
6203431Scarlsonj 				lif->lif_declined = NULL;
6213431Scarlsonj 			}
6229629Sjames.d.carlson@sun.com 			if (lif->lif_dad_wait) {
6239629Sjames.d.carlson@sun.com 				lif->lif_dad_wait = _B_FALSE;
6249629Sjames.d.carlson@sun.com 				dlp->dl_smach->dsm_lif_wait--;
6259629Sjames.d.carlson@sun.com 			}
6263431Scarlsonj 			lif->lif_lease = NULL;
6273431Scarlsonj 			release_lif(lif);
6283431Scarlsonj 		}
6293431Scarlsonj 	}
6303431Scarlsonj }
6313431Scarlsonj 
6323431Scarlsonj /*
6333431Scarlsonj  * lookup_lif_by_name(): Looks up a logical interface entry given a name and
6343431Scarlsonj  *			 a physical interface.
6353431Scarlsonj  *
6363431Scarlsonj  *   input: const char *: the logical interface name
6373431Scarlsonj  *	    const dhcp_pif_t *: the physical interface
6383431Scarlsonj  *  output: dhcp_lif_t *: the matching LIF, or NULL if not found
6393431Scarlsonj  */
6403431Scarlsonj 
6413431Scarlsonj dhcp_lif_t *
lookup_lif_by_name(const char * lname,const dhcp_pif_t * pif)6423431Scarlsonj lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif)
6433431Scarlsonj {
6443431Scarlsonj 	dhcp_lif_t *lif;
6453431Scarlsonj 
6463431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
6473431Scarlsonj 		if (strcmp(lif->lif_name, lname) == 0)
6483431Scarlsonj 			break;
6493431Scarlsonj 	}
6503431Scarlsonj 
6513431Scarlsonj 	return (lif);
6523431Scarlsonj }
6533431Scarlsonj 
6543431Scarlsonj /*
6553431Scarlsonj  * checkaddr(): checks if the given address is still set on the given LIF
6563431Scarlsonj  *
6573431Scarlsonj  *   input: const dhcp_lif_t *: the LIF to check
6583431Scarlsonj  *	    int: the address to look up on the interface (ioctl)
6593431Scarlsonj  *	    const in6_addr_t *: the address to compare to
6603431Scarlsonj  *	    const char *: name of the address for logging purposes
6613431Scarlsonj  *  output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
6623431Scarlsonj  */
6633431Scarlsonj 
6643431Scarlsonj static boolean_t
checkaddr(const dhcp_lif_t * lif,int ioccmd,const in6_addr_t * addr,const char * aname)6653431Scarlsonj checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr,
6663431Scarlsonj     const char *aname)
6673431Scarlsonj {
6683431Scarlsonj 	boolean_t isv6;
6693431Scarlsonj 	int fd;
6703431Scarlsonj 	struct lifreq lifr;
6715381Smeem 	char abuf1[INET6_ADDRSTRLEN];
6725381Smeem 	char abuf2[INET6_ADDRSTRLEN];
6733431Scarlsonj 
6743431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
6753431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
6763431Scarlsonj 
6773431Scarlsonj 	isv6 = lif->lif_pif->pif_isv6;
6783431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
6793431Scarlsonj 
6803431Scarlsonj 	if (ioctl(fd, ioccmd, &lifr) == -1) {
6813431Scarlsonj 		if (errno == ENXIO) {
6823431Scarlsonj 			dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone",
6833431Scarlsonj 			    lif->lif_name);
6843431Scarlsonj 			return (B_FALSE);
6853431Scarlsonj 		}
6863431Scarlsonj 		dhcpmsg(MSG_DEBUG,
6873431Scarlsonj 		    "checkaddr: ignoring ioctl error on %s %x: %s",
6883431Scarlsonj 		    lif->lif_name, ioccmd, strerror(errno));
6893431Scarlsonj 	} else if (isv6) {
6903431Scarlsonj 		struct sockaddr_in6 *sin6 =
6913431Scarlsonj 		    (struct sockaddr_in6 *)&lifr.lifr_addr;
6923431Scarlsonj 
6933431Scarlsonj 		if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) {
6943431Scarlsonj 			dhcpmsg(MSG_WARNING,
6955381Smeem 			    "checkaddr: expected %s %s on %s, have %s", aname,
6965381Smeem 			    inet_ntop(AF_INET6, addr, abuf1, sizeof (abuf1)),
6975381Smeem 			    lif->lif_name, inet_ntop(AF_INET6, &sin6->sin6_addr,
6985381Smeem 			    abuf2, sizeof (abuf2)));
6993431Scarlsonj 			return (B_FALSE);
7003431Scarlsonj 		}
7013431Scarlsonj 	} else {
7023431Scarlsonj 		struct sockaddr_in *sinp =
7033431Scarlsonj 		    (struct sockaddr_in *)&lifr.lifr_addr;
7043431Scarlsonj 		ipaddr_t v4addr;
7053431Scarlsonj 
7063431Scarlsonj 		IN6_V4MAPPED_TO_IPADDR(addr, v4addr);
7073431Scarlsonj 		if (sinp->sin_addr.s_addr != v4addr) {
7083431Scarlsonj 			dhcpmsg(MSG_WARNING,
7095381Smeem 			    "checkaddr: expected %s %s on %s, have %s", aname,
7105381Smeem 			    inet_ntop(AF_INET, &v4addr, abuf1, sizeof (abuf1)),
7115381Smeem 			    lif->lif_name, inet_ntop(AF_INET, &sinp->sin_addr,
7125381Smeem 			    abuf2, sizeof (abuf2)));
7133431Scarlsonj 			return (B_FALSE);
7143431Scarlsonj 		}
7153431Scarlsonj 	}
7163431Scarlsonj 	return (B_TRUE);
7173431Scarlsonj }
7183431Scarlsonj 
7193431Scarlsonj /*
7203431Scarlsonj  * verify_lif(): verifies than a LIF is still valid (i.e., has not been
7213431Scarlsonj  *		 explicitly or implicitly dropped or released)
7223431Scarlsonj  *
7233431Scarlsonj  *   input: const dhcp_lif_t *: the LIF to verify
7243431Scarlsonj  *  output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise
7253431Scarlsonj  */
7263431Scarlsonj 
7273431Scarlsonj boolean_t
verify_lif(const dhcp_lif_t * lif)7283431Scarlsonj verify_lif(const dhcp_lif_t *lif)
7293431Scarlsonj {
7303431Scarlsonj 	boolean_t isv6;
7313431Scarlsonj 	int fd;
7323431Scarlsonj 	struct lifreq lifr;
7338485SPeter.Memishian@Sun.COM 	dhcp_pif_t *pif = lif->lif_pif;
7343431Scarlsonj 
7353431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
7363431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
7373431Scarlsonj 
7388485SPeter.Memishian@Sun.COM 	isv6 = pif->pif_isv6;
7393431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
7403431Scarlsonj 
7413431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
7424106Scarlsonj 		if (errno != ENXIO) {
7434106Scarlsonj 			dhcpmsg(MSG_ERR,
7444106Scarlsonj 			    "verify_lif: SIOCGLIFFLAGS failed on %s",
7454106Scarlsonj 			    lif->lif_name);
7464106Scarlsonj 		}
7473431Scarlsonj 		return (B_FALSE);
7483431Scarlsonj 	}
7493431Scarlsonj 
7503431Scarlsonj 	/*
7513431Scarlsonj 	 * If important flags have changed, then abandon the interface.
7523431Scarlsonj 	 */
7533431Scarlsonj 	if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) {
7543431Scarlsonj 		dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: "
7553431Scarlsonj 		    "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags,
7563431Scarlsonj 		    lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) &
7573431Scarlsonj 		    DHCP_IFF_WATCH);
7583431Scarlsonj 		return (B_FALSE);
7593431Scarlsonj 	}
7603431Scarlsonj 
7613431Scarlsonj 	/*
7628485SPeter.Memishian@Sun.COM 	 * Check for delete and recreate.
7633431Scarlsonj 	 */
7648485SPeter.Memishian@Sun.COM 	if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
7658485SPeter.Memishian@Sun.COM 		if (errno != ENXIO) {
7668485SPeter.Memishian@Sun.COM 			dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed "
7678485SPeter.Memishian@Sun.COM 			    "on %s", lif->lif_name);
7688485SPeter.Memishian@Sun.COM 		}
7698485SPeter.Memishian@Sun.COM 		return (B_FALSE);
7703431Scarlsonj 	}
7718485SPeter.Memishian@Sun.COM 	if (lifr.lifr_index != pif->pif_index) {
7728485SPeter.Memishian@Sun.COM 		dhcpmsg(MSG_DEBUG,
7738485SPeter.Memishian@Sun.COM 		    "verify_lif: ifindex on %s changed: %u to %u",
7748485SPeter.Memishian@Sun.COM 		    lif->lif_name, pif->pif_index, lifr.lifr_index);
7753431Scarlsonj 		return (B_FALSE);
7763431Scarlsonj 	}
7773431Scarlsonj 
7788485SPeter.Memishian@Sun.COM 	if (pif->pif_under_ipmp) {
7798485SPeter.Memishian@Sun.COM 		(void) strlcpy(lifr.lifr_name, pif->pif_grifname, LIFNAMSIZ);
7808485SPeter.Memishian@Sun.COM 
7818485SPeter.Memishian@Sun.COM 		if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
7828485SPeter.Memishian@Sun.COM 			if (errno != ENXIO) {
7838485SPeter.Memishian@Sun.COM 				dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX "
7848485SPeter.Memishian@Sun.COM 				    "failed on %s", lifr.lifr_name);
7858485SPeter.Memishian@Sun.COM 			}
7868485SPeter.Memishian@Sun.COM 			return (B_FALSE);
7878485SPeter.Memishian@Sun.COM 		}
7888485SPeter.Memishian@Sun.COM 
7898485SPeter.Memishian@Sun.COM 		if (lifr.lifr_index != pif->pif_grindex) {
7908485SPeter.Memishian@Sun.COM 			dhcpmsg(MSG_DEBUG, "verify_lif: IPMP group ifindex "
7918485SPeter.Memishian@Sun.COM 			    "on %s changed: %u to %u", lifr.lifr_name,
7928485SPeter.Memishian@Sun.COM 			    pif->pif_grindex, lifr.lifr_index);
7938485SPeter.Memishian@Sun.COM 			return (B_FALSE);
7948485SPeter.Memishian@Sun.COM 		}
7953431Scarlsonj 	}
7963431Scarlsonj 
7973431Scarlsonj 	/*
7983431Scarlsonj 	 * If the IP address, netmask, or broadcast address have changed, or
7993431Scarlsonj 	 * the interface has been unplumbed, then we act like there has been an
8003431Scarlsonj 	 * implicit drop.  (Note that the netmask is under DHCP control for
8013431Scarlsonj 	 * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast
8023431Scarlsonj 	 * addresses.)
8033431Scarlsonj 	 */
8042546Scarlsonj 
8053431Scarlsonj 	if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address"))
8063431Scarlsonj 		return (B_FALSE);
8073431Scarlsonj 
8083431Scarlsonj 	if (isv6) {
8093431Scarlsonj 		/*
8103431Scarlsonj 		 * If it's not point-to-point, we're done.  If it is, then
8113431Scarlsonj 		 * check the peer's address as well.
8123431Scarlsonj 		 */
8133431Scarlsonj 		return (!(lif->lif_flags & IFF_POINTOPOINT) ||
8143431Scarlsonj 		    checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer,
8153431Scarlsonj 		    "peer address"));
8163431Scarlsonj 	} else {
8173431Scarlsonj 		if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask,
8183431Scarlsonj 		    "netmask"))
8193431Scarlsonj 			return (B_FALSE);
8203431Scarlsonj 
8213431Scarlsonj 		return (checkaddr(lif,
8223431Scarlsonj 		    (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR :
8233431Scarlsonj 		    SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address"));
8243431Scarlsonj 	}
8253431Scarlsonj }
8263431Scarlsonj 
8273431Scarlsonj /*
8283431Scarlsonj  * canonize_lif(): puts the interface in a canonical (zeroed) form.  This is
8293431Scarlsonj  *		   used only on the "main" LIF for IPv4.  All other interfaces
8303431Scarlsonj  *		   are under dhcpagent control and are removed using
8313431Scarlsonj  *		   unplumb_lif().
8323431Scarlsonj  *
8333431Scarlsonj  *   input: dhcp_lif_t *: the interface to canonize
8345381Smeem  *	    boolean_t: only canonize lif if it's under DHCP control
8353431Scarlsonj  *  output: none
8363431Scarlsonj  */
8373431Scarlsonj 
8383431Scarlsonj static void
canonize_lif(dhcp_lif_t * lif,boolean_t dhcponly)8395381Smeem canonize_lif(dhcp_lif_t *lif, boolean_t dhcponly)
8403431Scarlsonj {
8413431Scarlsonj 	boolean_t isv6;
8423431Scarlsonj 	int fd;
8433431Scarlsonj 	struct lifreq lifr;
8443431Scarlsonj 
8453431Scarlsonj 	/*
8463431Scarlsonj 	 * If there's nothing here, then don't touch the interface.  This can
8473431Scarlsonj 	 * happen when an already-canonized LIF is recanonized.
8483431Scarlsonj 	 */
8493431Scarlsonj 	if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr))
8503431Scarlsonj 		return;
8513431Scarlsonj 
8523431Scarlsonj 	isv6 = lif->lif_pif->pif_isv6;
8533431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s",
8543431Scarlsonj 	    isv6 ? 6 : 4, lif->lif_name);
8553431Scarlsonj 
8563431Scarlsonj 	lif->lif_v6addr = my_in6addr_any;
8573431Scarlsonj 	lif->lif_v6mask = my_in6addr_any;
8583431Scarlsonj 	lif->lif_v6peer = my_in6addr_any;
8593431Scarlsonj 
8603431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
8613431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
8623431Scarlsonj 
8633431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
8643431Scarlsonj 
8653431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
8664106Scarlsonj 		if (errno != ENXIO) {
8674106Scarlsonj 			dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s",
8684106Scarlsonj 			    lif->lif_name);
8694106Scarlsonj 		}
8703431Scarlsonj 		return;
8713431Scarlsonj 	}
8726637Smeem 	lif->lif_flags = lifr.lifr_flags;
8733431Scarlsonj 
8745381Smeem 	if (dhcponly && !(lifr.lifr_flags & IFF_DHCPRUNNING)) {
8753431Scarlsonj 		dhcpmsg(MSG_INFO,
8763431Scarlsonj 		    "canonize_lif: cannot clear %s; flags are %llx",
8773431Scarlsonj 		    lif->lif_name, lifr.lifr_flags);
8783431Scarlsonj 		return;
8793431Scarlsonj 	}
8803431Scarlsonj 
8813431Scarlsonj 	(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
8823431Scarlsonj 	if (isv6) {
8833431Scarlsonj 		struct sockaddr_in6 *sin6 =
8843431Scarlsonj 		    (struct sockaddr_in6 *)&lifr.lifr_addr;
8853431Scarlsonj 
8863431Scarlsonj 		sin6->sin6_family = AF_INET6;
8873431Scarlsonj 		sin6->sin6_addr = my_in6addr_any;
8883431Scarlsonj 	} else {
8893431Scarlsonj 		struct sockaddr_in *sinv =
8903431Scarlsonj 		    (struct sockaddr_in *)&lifr.lifr_addr;
8913431Scarlsonj 
8923431Scarlsonj 		sinv->sin_family = AF_INET;
8933431Scarlsonj 		sinv->sin_addr.s_addr = htonl(INADDR_ANY);
8943431Scarlsonj 	}
8953431Scarlsonj 
8963431Scarlsonj 	if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) {
8973431Scarlsonj 		dhcpmsg(MSG_ERR,
8983431Scarlsonj 		    "canonize_lif: can't clear local address on %s",
8993431Scarlsonj 		    lif->lif_name);
9003431Scarlsonj 	}
9013431Scarlsonj 
9029629Sjames.d.carlson@sun.com 	/* Clearing the address means that we're no longer waiting on DAD */
9039629Sjames.d.carlson@sun.com 	if (lif->lif_dad_wait) {
9049629Sjames.d.carlson@sun.com 		lif->lif_dad_wait = _B_FALSE;
9059629Sjames.d.carlson@sun.com 		lif->lif_lease->dl_smach->dsm_lif_wait--;
9069629Sjames.d.carlson@sun.com 	}
9079629Sjames.d.carlson@sun.com 
9083431Scarlsonj 	if (lif->lif_flags & IFF_POINTOPOINT) {
9093431Scarlsonj 		if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) {
9103431Scarlsonj 			dhcpmsg(MSG_ERR,
9113431Scarlsonj 			    "canonize_lif: can't clear remote address on %s",
9123431Scarlsonj 			    lif->lif_name);
9133431Scarlsonj 		}
9143431Scarlsonj 	} else if (!isv6) {
9153431Scarlsonj 		if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) {
9163431Scarlsonj 			dhcpmsg(MSG_ERR,
9173431Scarlsonj 			    "canonize_lif: can't clear broadcast address on %s",
9183431Scarlsonj 			    lif->lif_name);
9193431Scarlsonj 		}
9203431Scarlsonj 	}
9218403SAnurag.Maskey@Sun.COM 
9228403SAnurag.Maskey@Sun.COM 	/*
9238403SAnurag.Maskey@Sun.COM 	 * Clear the netmask last as it has to be refetched after clearing.
9248403SAnurag.Maskey@Sun.COM 	 * Netmask is under in.ndpd control with IPv6.
9258403SAnurag.Maskey@Sun.COM 	 */
9268403SAnurag.Maskey@Sun.COM 	if (!isv6) {
9278403SAnurag.Maskey@Sun.COM 		/* Clear the netmask */
9288403SAnurag.Maskey@Sun.COM 		if (ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) {
9298403SAnurag.Maskey@Sun.COM 			dhcpmsg(MSG_ERR,
9308403SAnurag.Maskey@Sun.COM 			    "canonize_lif: can't clear netmask on %s",
9318403SAnurag.Maskey@Sun.COM 			    lif->lif_name);
9328403SAnurag.Maskey@Sun.COM 		} else  {
9338403SAnurag.Maskey@Sun.COM 			/*
9348403SAnurag.Maskey@Sun.COM 			 * When the netmask is cleared, the kernel actually sets
9358403SAnurag.Maskey@Sun.COM 			 * the netmask to 255.0.0.0.  So, refetch that netmask.
9368403SAnurag.Maskey@Sun.COM 			 */
9378403SAnurag.Maskey@Sun.COM 			if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) {
9388403SAnurag.Maskey@Sun.COM 				dhcpmsg(MSG_ERR,
9398403SAnurag.Maskey@Sun.COM 				    "canonize_lif: can't reload cleared "
9408403SAnurag.Maskey@Sun.COM 				    "netmask on %s", lif->lif_name);
9418403SAnurag.Maskey@Sun.COM 			} else {
9428403SAnurag.Maskey@Sun.COM 				/* Refetch succeeded, update LIF */
9438403SAnurag.Maskey@Sun.COM 				lif->lif_netmask =
9448403SAnurag.Maskey@Sun.COM 				    ((struct sockaddr_in *)&lifr.lifr_addr)->
9458403SAnurag.Maskey@Sun.COM 				    sin_addr.s_addr;
9468403SAnurag.Maskey@Sun.COM 			}
9478403SAnurag.Maskey@Sun.COM 		}
9488403SAnurag.Maskey@Sun.COM 	}
9493431Scarlsonj }
9503431Scarlsonj 
9513431Scarlsonj /*
9523431Scarlsonj  * plumb_lif(): Adds the LIF to the system.  This is used for all
9533431Scarlsonj  *		DHCPv6-derived interfaces.  The returned LIF has a hold
9549629Sjames.d.carlson@sun.com  *		on it.  The caller (configure_v6_leases) deals with the DAD
9559629Sjames.d.carlson@sun.com  *		wait counters.
9563431Scarlsonj  *
9573431Scarlsonj  *   input: dhcp_lif_t *: the interface to unplumb
9583431Scarlsonj  *  output: none
9593431Scarlsonj  */
9603431Scarlsonj 
9613431Scarlsonj dhcp_lif_t *
plumb_lif(dhcp_pif_t * pif,const in6_addr_t * addr)9623431Scarlsonj plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr)
9633431Scarlsonj {
9643431Scarlsonj 	dhcp_lif_t *lif;
9653431Scarlsonj 	char abuf[INET6_ADDRSTRLEN];
9663431Scarlsonj 	struct lifreq lifr;
9673431Scarlsonj 	struct sockaddr_in6 *sin6;
9683431Scarlsonj 	int error;
9693431Scarlsonj 
9703431Scarlsonj 	(void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf));
9713431Scarlsonj 
9723431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
9733431Scarlsonj 		if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) {
9743431Scarlsonj 			dhcpmsg(MSG_ERR,
9753431Scarlsonj 			    "plumb_lif: entry for %s already exists!", abuf);
9763431Scarlsonj 			return (NULL);
9773431Scarlsonj 		}
9783431Scarlsonj 	}
9793431Scarlsonj 
9803431Scarlsonj 	/* First, create a new zero-address logical interface */
9813431Scarlsonj 	(void) memset(&lifr, 0, sizeof (lifr));
9823431Scarlsonj 	(void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name));
9833431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) {
9843431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name);
9853431Scarlsonj 		return (NULL);
9863431Scarlsonj 	}
9873431Scarlsonj 
9883431Scarlsonj 	/* Next, set the netmask to all ones */
9893431Scarlsonj 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
9903431Scarlsonj 	sin6->sin6_family = AF_INET6;
9913431Scarlsonj 	(void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr));
9923431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
9933431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s",
9943431Scarlsonj 		    lifr.lifr_name);
9950Sstevel@tonic-gate 		goto failure;
9960Sstevel@tonic-gate 	}
9970Sstevel@tonic-gate 
9983431Scarlsonj 	/* Now set the interface address */
9993431Scarlsonj 	sin6->sin6_addr = *addr;
10003431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
10013431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s",
10023431Scarlsonj 		    lifr.lifr_name, abuf);
10033431Scarlsonj 		goto failure;
10043431Scarlsonj 	}
10053431Scarlsonj 
10063431Scarlsonj 	/* Mark the interface up */
10073431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
10083431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s",
10093431Scarlsonj 		    lifr.lifr_name);
10103431Scarlsonj 		goto failure;
10113431Scarlsonj 	}
10128485SPeter.Memishian@Sun.COM 
10138485SPeter.Memishian@Sun.COM 	/*
10148485SPeter.Memishian@Sun.COM 	 * See comment in set_lif_dhcp().
10158485SPeter.Memishian@Sun.COM 	 */
10168485SPeter.Memishian@Sun.COM 	if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER))
10178485SPeter.Memishian@Sun.COM 		lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
10188485SPeter.Memishian@Sun.COM 
10193431Scarlsonj 	lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING;
10203431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
10213431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s",
10223431Scarlsonj 		    lifr.lifr_name);
10233431Scarlsonj 		goto failure;
10243431Scarlsonj 	}
10253431Scarlsonj 
10263431Scarlsonj 	/* Now we can create the internal LIF structure */
10273431Scarlsonj 	hold_pif(pif);
10283431Scarlsonj 	if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL)
10293431Scarlsonj 		goto failure;
10303431Scarlsonj 
10313431Scarlsonj 	dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf,
10323431Scarlsonj 	    lif->lif_name);
10333431Scarlsonj 	lif->lif_plumbed = B_TRUE;
10343431Scarlsonj 
10353431Scarlsonj 	return (lif);
10363431Scarlsonj 
10373431Scarlsonj failure:
10383431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
10393431Scarlsonj 	    errno != ENXIO) {
10403431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s",
10413431Scarlsonj 		    lifr.lifr_name);
10423431Scarlsonj 	}
10433431Scarlsonj 	return (NULL);
10443431Scarlsonj }
10453431Scarlsonj 
10463431Scarlsonj /*
10473431Scarlsonj  * unplumb_lif(): Removes the LIF from dhcpagent and the system.  This is used
10483431Scarlsonj  *		  for all interfaces configured by DHCP (those in leases).
10493431Scarlsonj  *
10503431Scarlsonj  *   input: dhcp_lif_t *: the interface to unplumb
10513431Scarlsonj  *  output: none
10523431Scarlsonj  */
10533431Scarlsonj 
10543431Scarlsonj void
unplumb_lif(dhcp_lif_t * lif)10553431Scarlsonj unplumb_lif(dhcp_lif_t *lif)
10563431Scarlsonj {
10573431Scarlsonj 	dhcp_lease_t *dlp;
10583431Scarlsonj 
10593431Scarlsonj 	if (lif->lif_plumbed) {
10603431Scarlsonj 		struct lifreq lifr;
10613431Scarlsonj 
10623431Scarlsonj 		(void) memset(&lifr, 0, sizeof (lifr));
10633431Scarlsonj 		(void) strlcpy(lifr.lifr_name, lif->lif_name,
10643431Scarlsonj 		    sizeof (lifr.lifr_name));
10653431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
10663431Scarlsonj 		    errno != ENXIO) {
10673431Scarlsonj 			dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s",
10683431Scarlsonj 			    lif->lif_name);
10693431Scarlsonj 		}
10703431Scarlsonj 		lif->lif_plumbed = B_FALSE;
10713431Scarlsonj 	}
10725978Smeem 
10733431Scarlsonj 	/*
10743431Scarlsonj 	 * Special case: if we're "unplumbing" the main LIF for DHCPv4, then
10759629Sjames.d.carlson@sun.com 	 * just canonize it and remove it from the lease.  The DAD wait flags
10769629Sjames.d.carlson@sun.com 	 * are handled by canonize_lif or by remove_lif.
10773431Scarlsonj 	 */
10783431Scarlsonj 	if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) {
10795381Smeem 		canonize_lif(lif, B_TRUE);
10803431Scarlsonj 		cancel_lif_timers(lif);
10813431Scarlsonj 		if (lif->lif_declined != NULL) {
10823431Scarlsonj 			dlp->dl_smach->dsm_lif_down--;
10833431Scarlsonj 			lif->lif_declined = NULL;
10843431Scarlsonj 		}
10853431Scarlsonj 		dlp->dl_nlifs = 0;
10863431Scarlsonj 		dlp->dl_lifs = NULL;
10873431Scarlsonj 		lif->lif_lease = NULL;
10883431Scarlsonj 		release_lif(lif);
10893431Scarlsonj 	} else {
10903431Scarlsonj 		remove_lif(lif);
10913431Scarlsonj 	}
10923431Scarlsonj }
10933431Scarlsonj 
10943431Scarlsonj /*
10953431Scarlsonj  * attach_lif(): create a new logical interface, creating the physical
10963431Scarlsonj  *		 interface as necessary.
10973431Scarlsonj  *
10983431Scarlsonj  *   input: const char *: the logical interface name
10993431Scarlsonj  *	    boolean_t: B_TRUE for IPv6
11003431Scarlsonj  *	    int *: set to DHCP_IPC_E_* if creation fails
11013431Scarlsonj  *  output: dhcp_lif_t *: pointer to new entry, or NULL on failure
11023431Scarlsonj  */
11033431Scarlsonj 
11043431Scarlsonj dhcp_lif_t *
attach_lif(const char * lname,boolean_t isv6,int * error)11053431Scarlsonj attach_lif(const char *lname, boolean_t isv6, int *error)
11063431Scarlsonj {
11073431Scarlsonj 	dhcp_pif_t *pif;
11083431Scarlsonj 	char pname[LIFNAMSIZ], *cp;
11093431Scarlsonj 
11103431Scarlsonj 	(void) strlcpy(pname, lname, sizeof (pname));
11113431Scarlsonj 	if ((cp = strchr(pname, ':')) != NULL)
11123431Scarlsonj 		*cp = '\0';
11133431Scarlsonj 
11143431Scarlsonj 	if ((pif = lookup_pif_by_name(pname, isv6)) != NULL)
11153431Scarlsonj 		hold_pif(pif);
11163431Scarlsonj 	else if ((pif = insert_pif(pname, isv6, error)) == NULL)
11173431Scarlsonj 		return (NULL);
11183431Scarlsonj 
11193431Scarlsonj 	if (lookup_lif_by_name(lname, pif) != NULL) {
11203431Scarlsonj 		dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!",
11213431Scarlsonj 		    lname);
11223431Scarlsonj 		release_pif(pif);
11233431Scarlsonj 		*error = DHCP_IPC_E_INVIF;
11243431Scarlsonj 		return (NULL);
11253431Scarlsonj 	}
11263431Scarlsonj 
11273431Scarlsonj 	/* If LIF creation fails, then insert_lif discards our PIF hold */
11283431Scarlsonj 	return (insert_lif(pif, lname, error));
11293431Scarlsonj }
11303431Scarlsonj 
11313431Scarlsonj /*
11323431Scarlsonj  * set_lif_dhcp(): Set logical interface flags to show that it's managed
11333431Scarlsonj  *		   by DHCP.
11343431Scarlsonj  *
11353431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
11363431Scarlsonj  *  output: int: set to DHCP_IPC_E_* if operation fails
11373431Scarlsonj  */
11383431Scarlsonj 
11393431Scarlsonj int
set_lif_dhcp(dhcp_lif_t * lif)114010785SPeter.Memishian@Sun.COM set_lif_dhcp(dhcp_lif_t *lif)
11413431Scarlsonj {
11423431Scarlsonj 	int fd;
11433431Scarlsonj 	int err;
11443431Scarlsonj 	struct lifreq lifr;
11458485SPeter.Memishian@Sun.COM 	dhcp_pif_t *pif = lif->lif_pif;
11463431Scarlsonj 
11478485SPeter.Memishian@Sun.COM 	fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
11483431Scarlsonj 
11493431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
11503431Scarlsonj 
11513431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
11523431Scarlsonj 		err = errno;
11533431Scarlsonj 		dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s",
11543431Scarlsonj 		    lif->lif_name);
11553431Scarlsonj 		return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT);
11563431Scarlsonj 	}
11573431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
11583431Scarlsonj 
11593431Scarlsonj 	/*
11603431Scarlsonj 	 * Check for conflicting sources of address control, and other
11613431Scarlsonj 	 * unacceptable configurations.
11623431Scarlsonj 	 */
11634823Sseb 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
11644823Sseb 	    IFF_VIRTUAL)) {
11653431Scarlsonj 		dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx",
11663431Scarlsonj 		    lif->lif_name, lifr.lifr_flags);
11673431Scarlsonj 		return (DHCP_IPC_E_INVIF);
11683431Scarlsonj 	}
11693431Scarlsonj 
11700Sstevel@tonic-gate 	/*
117110785SPeter.Memishian@Sun.COM 	 * If IFF_DHCPRUNNING is already set on the interface and we're not
117210785SPeter.Memishian@Sun.COM 	 * adopting it, the agent probably crashed and burned.  Note it, but
117310785SPeter.Memishian@Sun.COM 	 * don't let it stop the proceedings (we're pretty sure we're not
117410785SPeter.Memishian@Sun.COM 	 * already running, since we were able to bind to our IPC port).
11750Sstevel@tonic-gate 	 */
11763431Scarlsonj 	if (lifr.lifr_flags & IFF_DHCPRUNNING) {
117710785SPeter.Memishian@Sun.COM 		dhcpmsg(MSG_VERBOSE, "set_lif_dhcp: IFF_DHCPRUNNING already set"
117810785SPeter.Memishian@Sun.COM 		    " on %s", lif->lif_name);
11793431Scarlsonj 	} else {
11808485SPeter.Memishian@Sun.COM 		/*
11818485SPeter.Memishian@Sun.COM 		 * If the lif is on an interface under IPMP, IFF_NOFAILOVER
11828485SPeter.Memishian@Sun.COM 		 * must be set or the kernel will prevent us from setting
11838485SPeter.Memishian@Sun.COM 		 * IFF_DHCPRUNNING (since the subsequent IFF_UP would lead to
11848485SPeter.Memishian@Sun.COM 		 * migration).  We set IFF_DEPRECATED too since the kernel
11858485SPeter.Memishian@Sun.COM 		 * will set it automatically when setting IFF_NOFAILOVER,
11868485SPeter.Memishian@Sun.COM 		 * causing our lif_flags value to grow stale.
11878485SPeter.Memishian@Sun.COM 		 */
11888485SPeter.Memishian@Sun.COM 		if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER))
11898485SPeter.Memishian@Sun.COM 			lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
11908485SPeter.Memishian@Sun.COM 
11913431Scarlsonj 		lifr.lifr_flags |= IFF_DHCPRUNNING;
11923431Scarlsonj 		if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
11933431Scarlsonj 			dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s",
11943431Scarlsonj 			    lif->lif_name);
11953431Scarlsonj 			return (DHCP_IPC_E_INT);
11963431Scarlsonj 		}
11973431Scarlsonj 		lif->lif_flags = lifr.lifr_flags;
11980Sstevel@tonic-gate 	}
11993431Scarlsonj 	return (DHCP_IPC_SUCCESS);
12003431Scarlsonj }
12010Sstevel@tonic-gate 
12023431Scarlsonj /*
12033431Scarlsonj  * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer
12043431Scarlsonj  *		     managed by DHCP.
12053431Scarlsonj  *
12063431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
12073431Scarlsonj  *  output: none
12083431Scarlsonj  */
12090Sstevel@tonic-gate 
12103431Scarlsonj static void
clear_lif_dhcp(dhcp_lif_t * lif)12113431Scarlsonj clear_lif_dhcp(dhcp_lif_t *lif)
12123431Scarlsonj {
12133431Scarlsonj 	int fd;
12143431Scarlsonj 	struct lifreq lifr;
12150Sstevel@tonic-gate 
12163431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
12173431Scarlsonj 
12183431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
12193431Scarlsonj 
12203431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
12213431Scarlsonj 		return;
12220Sstevel@tonic-gate 
12233431Scarlsonj 	if (!(lifr.lifr_flags & IFF_DHCPRUNNING))
12243431Scarlsonj 		return;
12250Sstevel@tonic-gate 
12263431Scarlsonj 	lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING;
12273431Scarlsonj 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
12283431Scarlsonj }
12290Sstevel@tonic-gate 
12303431Scarlsonj /*
12313431Scarlsonj  * set_lif_deprecated(): Set the "deprecated" flag to tell users that this
12323431Scarlsonj  *			 address will be going away.  As the interface is
12333431Scarlsonj  *			 going away, we don't care if there are errors.
12343431Scarlsonj  *
12353431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
12363431Scarlsonj  *  output: none
12373431Scarlsonj  */
12380Sstevel@tonic-gate 
12393431Scarlsonj void
set_lif_deprecated(dhcp_lif_t * lif)12403431Scarlsonj set_lif_deprecated(dhcp_lif_t *lif)
12413431Scarlsonj {
12423431Scarlsonj 	int fd;
12433431Scarlsonj 	struct lifreq lifr;
12440Sstevel@tonic-gate 
12453431Scarlsonj 	if (lif->lif_flags & IFF_DEPRECATED)
12463431Scarlsonj 		return;
12470Sstevel@tonic-gate 
12483431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
12493431Scarlsonj 
12503431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
12510Sstevel@tonic-gate 
12523431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
12533431Scarlsonj 		return;
12543431Scarlsonj 
12553431Scarlsonj 	if (lifr.lifr_flags & IFF_DEPRECATED)
12563431Scarlsonj 		return;
12570Sstevel@tonic-gate 
12583431Scarlsonj 	lifr.lifr_flags |= IFF_DEPRECATED;
12593431Scarlsonj 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
12603431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
12613431Scarlsonj }
12623431Scarlsonj 
12633431Scarlsonj /*
12643431Scarlsonj  * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this
12653431Scarlsonj  *			   address will not be going away.  This happens if we
12663431Scarlsonj  *			   get a renewal after preferred lifetime but before
12673431Scarlsonj  *			   the valid lifetime.
12683431Scarlsonj  *
12693431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
12703431Scarlsonj  *  output: boolean_t: B_TRUE on success.
12713431Scarlsonj  */
12720Sstevel@tonic-gate 
12733431Scarlsonj boolean_t
clear_lif_deprecated(dhcp_lif_t * lif)12743431Scarlsonj clear_lif_deprecated(dhcp_lif_t *lif)
12753431Scarlsonj {
12763431Scarlsonj 	int fd;
12773431Scarlsonj 	struct lifreq lifr;
12783431Scarlsonj 
12793431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
12803431Scarlsonj 
12813431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
12823431Scarlsonj 
12833431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
12843431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s",
12853431Scarlsonj 		    lif->lif_name);
12863431Scarlsonj 		return (B_FALSE);
12870Sstevel@tonic-gate 	}
12880Sstevel@tonic-gate 
12890Sstevel@tonic-gate 	/*
12903431Scarlsonj 	 * Check for conflicting sources of address control, and other
12913431Scarlsonj 	 * unacceptable configurations.
12920Sstevel@tonic-gate 	 */
12934823Sseb 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_TEMPORARY|
12944823Sseb 	    IFF_VIRTUAL)) {
12953431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags "
12963431Scarlsonj 		    "are %llx", lif->lif_name, lifr.lifr_flags);
12973431Scarlsonj 		return (B_FALSE);
12980Sstevel@tonic-gate 	}
12990Sstevel@tonic-gate 
13008485SPeter.Memishian@Sun.COM 	/*
13018485SPeter.Memishian@Sun.COM 	 * Don't try to clear IFF_DEPRECATED if this is a test address,
13028485SPeter.Memishian@Sun.COM 	 * since IPMP's use of IFF_DEPRECATED is not compatible with ours.
13038485SPeter.Memishian@Sun.COM 	 */
13048485SPeter.Memishian@Sun.COM 	if (lifr.lifr_flags & IFF_NOFAILOVER)
13058485SPeter.Memishian@Sun.COM 		return (B_TRUE);
13068485SPeter.Memishian@Sun.COM 
13073431Scarlsonj 	if (!(lifr.lifr_flags & IFF_DEPRECATED))
13083431Scarlsonj 		return (B_TRUE);
13090Sstevel@tonic-gate 
13103431Scarlsonj 	lifr.lifr_flags &= ~IFF_DEPRECATED;
13113431Scarlsonj 	if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
13123431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s",
13133431Scarlsonj 		    lif->lif_name);
13143431Scarlsonj 		return (B_FALSE);
13153431Scarlsonj 	} else {
13163431Scarlsonj 		lif->lif_flags = lifr.lifr_flags;
13173431Scarlsonj 		return (B_TRUE);
13180Sstevel@tonic-gate 	}
13190Sstevel@tonic-gate }
13200Sstevel@tonic-gate 
13210Sstevel@tonic-gate /*
13223431Scarlsonj  * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only).
13230Sstevel@tonic-gate  *
13243431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
13255381Smeem  *	    in_addr_t: the address the socket will be bound to (in hbo)
13268485SPeter.Memishian@Sun.COM  *	    boolean_t: B_TRUE if the address should be brought up (if needed)
13273431Scarlsonj  *  output: boolean_t: B_TRUE if the socket was opened successfully.
13280Sstevel@tonic-gate  */
13290Sstevel@tonic-gate 
13303431Scarlsonj boolean_t
open_ip_lif(dhcp_lif_t * lif,in_addr_t addr_hbo,boolean_t bringup)13318485SPeter.Memishian@Sun.COM open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo, boolean_t bringup)
13320Sstevel@tonic-gate {
13335381Smeem 	const char *errmsg;
13345381Smeem 	struct lifreq lifr;
13355381Smeem 	int on = 1;
13365455Smeem 	uchar_t ttl = 255;
13378485SPeter.Memishian@Sun.COM 	uint32_t ifindex;
13388485SPeter.Memishian@Sun.COM 	dhcp_pif_t *pif = lif->lif_pif;
13395381Smeem 
13403431Scarlsonj 	if (lif->lif_sock_ip_fd != -1) {
13413431Scarlsonj 		dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s",
13423431Scarlsonj 		    lif->lif_name);
13433431Scarlsonj 		return (B_FALSE);
13440Sstevel@tonic-gate 	}
13450Sstevel@tonic-gate 
13463431Scarlsonj 	lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
13473431Scarlsonj 	if (lif->lif_sock_ip_fd == -1) {
13485381Smeem 		errmsg = "cannot create v4 socket";
13495381Smeem 		goto failure;
13505381Smeem 	}
13515381Smeem 
13525381Smeem 	if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC, addr_hbo)) {
13535381Smeem 		errmsg = "cannot bind v4 socket";
13545381Smeem 		goto failure;
13555381Smeem 	}
13565381Smeem 
13575381Smeem 	/*
13585381Smeem 	 * If we bound to INADDR_ANY, we have no IFF_UP source address to use.
13595381Smeem 	 * Thus, enable IP_UNSPEC_SRC so that we can send packets with an
13605381Smeem 	 * unspecified (0.0.0.0) address.  Also, enable IP_DHCPINIT_IF so that
13615381Smeem 	 * the IP module will accept unicast DHCP traffic regardless of the IP
13625381Smeem 	 * address it's sent to.  (We'll then figure out which packets are
13635381Smeem 	 * ours based on the xid.)
13645381Smeem 	 */
13655381Smeem 	if (addr_hbo == INADDR_ANY) {
13665381Smeem 		if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_UNSPEC_SRC,
13675381Smeem 		    &on, sizeof (int)) == -1) {
13685381Smeem 			errmsg = "cannot set IP_UNSPEC_SRC";
13695381Smeem 			goto failure;
13705381Smeem 		}
13715381Smeem 
13725381Smeem 		if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_DHCPINIT_IF,
13738485SPeter.Memishian@Sun.COM 		    &pif->pif_index, sizeof (int)) == -1) {
13745381Smeem 			errmsg = "cannot set IP_DHCPINIT_IF";
13755381Smeem 			goto failure;
13765381Smeem 		}
13775381Smeem 	}
13785381Smeem 
13795455Smeem 	/*
13805455Smeem 	 * Unfortunately, some hardware (such as the Linksys WRT54GC)
13815455Smeem 	 * decrements the TTL *prior* to accepting DHCP traffic destined
13825455Smeem 	 * for it.  To workaround this, tell IP to use a TTL of 255 for
13835455Smeem 	 * broadcast packets sent from this socket.
13845455Smeem 	 */
13855455Smeem 	if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BROADCAST_TTL, &ttl,
13865455Smeem 	    sizeof (uchar_t)) == -1) {
13875455Smeem 		errmsg = "cannot set IP_BROADCAST_TTL";
13885455Smeem 		goto failure;
13895455Smeem 	}
13905455Smeem 
13918485SPeter.Memishian@Sun.COM 	ifindex = pif->pif_under_ipmp ? pif->pif_grindex : pif->pif_index;
13928485SPeter.Memishian@Sun.COM 	if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF, &ifindex,
13938485SPeter.Memishian@Sun.COM 	    sizeof (int)) == -1) {
13945381Smeem 		errmsg = "cannot set IP_BOUND_IF";
13955381Smeem 		goto failure;
13960Sstevel@tonic-gate 	}
13970Sstevel@tonic-gate 
13985381Smeem 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
13995381Smeem 	if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
14005381Smeem 		errmsg = "cannot get interface flags";
14015381Smeem 		goto failure;
14023431Scarlsonj 	}
14030Sstevel@tonic-gate 
14048485SPeter.Memishian@Sun.COM 	/*
14058485SPeter.Memishian@Sun.COM 	 * If the lif is part of an interface under IPMP, IFF_NOFAILOVER must
14068485SPeter.Memishian@Sun.COM 	 * be set or the kernel will prevent us from setting IFF_DHCPRUNNING
14078485SPeter.Memishian@Sun.COM 	 * (since the subsequent IFF_UP would lead to migration).  We set
14088485SPeter.Memishian@Sun.COM 	 * IFF_DEPRECATED too since the kernel will set it automatically when
14098485SPeter.Memishian@Sun.COM 	 * setting IFF_NOFAILOVER, causing our lif_flags value to grow stale.
14108485SPeter.Memishian@Sun.COM 	 */
14118485SPeter.Memishian@Sun.COM 	if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER)) {
14128485SPeter.Memishian@Sun.COM 		lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
14138485SPeter.Memishian@Sun.COM 		if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
14148485SPeter.Memishian@Sun.COM 			errmsg = "cannot set IFF_NOFAILOVER";
14158485SPeter.Memishian@Sun.COM 			goto failure;
14168485SPeter.Memishian@Sun.COM 		}
14178485SPeter.Memishian@Sun.COM 	}
14188485SPeter.Memishian@Sun.COM 	lif->lif_flags = lifr.lifr_flags;
14198485SPeter.Memishian@Sun.COM 
14208485SPeter.Memishian@Sun.COM 	/*
14218485SPeter.Memishian@Sun.COM 	 * If this is initial bringup, make sure the address we're acquiring a
14228485SPeter.Memishian@Sun.COM 	 * lease on is IFF_UP.
14238485SPeter.Memishian@Sun.COM 	 */
14248485SPeter.Memishian@Sun.COM 	if (bringup && !(lifr.lifr_flags & IFF_UP)) {
14255381Smeem 		/*
14265381Smeem 		 * Start from a clean slate.
14275381Smeem 		 */
14285381Smeem 		canonize_lif(lif, B_FALSE);
14295381Smeem 
14305381Smeem 		lifr.lifr_flags |= IFF_UP;
14315381Smeem 		if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
14325381Smeem 			errmsg = "cannot bring up";
14335381Smeem 			goto failure;
14345381Smeem 		}
14355439Smeem 		lif->lif_flags = lifr.lifr_flags;
14365381Smeem 
14375381Smeem 		/*
14385381Smeem 		 * When bringing 0.0.0.0 IFF_UP, the kernel changes the
14395381Smeem 		 * netmask to 255.0.0.0, so re-fetch our expected netmask.
14405381Smeem 		 */
14415381Smeem 		if (ioctl(v4_sock_fd, SIOCGLIFNETMASK, &lifr) == -1) {
14425381Smeem 			errmsg = "cannot get netmask";
14435381Smeem 			goto failure;
14445381Smeem 		}
14455381Smeem 
14465381Smeem 		lif->lif_netmask =
14475381Smeem 		    ((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr;
14480Sstevel@tonic-gate 	}
14495381Smeem 
14508485SPeter.Memishian@Sun.COM 	/*
14518485SPeter.Memishian@Sun.COM 	 * Usually, bringing up the address we're acquiring a lease on is
14528485SPeter.Memishian@Sun.COM 	 * sufficient to allow packets to be sent and received via the
14538485SPeter.Memishian@Sun.COM 	 * IP_BOUND_IF we did earlier.  However, if we're acquiring a lease on
14548485SPeter.Memishian@Sun.COM 	 * an underlying IPMP interface, the group interface will be used for
14558485SPeter.Memishian@Sun.COM 	 * sending and receiving IP packets via IP_BOUND_IF.  Thus, ensure at
14568485SPeter.Memishian@Sun.COM 	 * least one address on the group interface is IFF_UP.
14578485SPeter.Memishian@Sun.COM 	 */
14588485SPeter.Memishian@Sun.COM 	if (bringup && pif->pif_under_ipmp) {
14598485SPeter.Memishian@Sun.COM 		(void) strlcpy(lifr.lifr_name, pif->pif_grifname, LIFNAMSIZ);
14608485SPeter.Memishian@Sun.COM 		if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
14618485SPeter.Memishian@Sun.COM 			errmsg = "cannot get IPMP group interface flags";
14628485SPeter.Memishian@Sun.COM 			goto failure;
14638485SPeter.Memishian@Sun.COM 		}
14648485SPeter.Memishian@Sun.COM 
14658485SPeter.Memishian@Sun.COM 		if (!(lifr.lifr_flags & IFF_UP)) {
14668485SPeter.Memishian@Sun.COM 			lifr.lifr_flags |= IFF_UP;
14678485SPeter.Memishian@Sun.COM 			if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
14688485SPeter.Memishian@Sun.COM 				errmsg = "cannot bring up IPMP group interface";
14698485SPeter.Memishian@Sun.COM 				goto failure;
14708485SPeter.Memishian@Sun.COM 			}
14718485SPeter.Memishian@Sun.COM 		}
14728485SPeter.Memishian@Sun.COM 	}
14738485SPeter.Memishian@Sun.COM 
14745381Smeem 	lif->lif_packet_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN,
14755381Smeem 	    dhcp_packet_lif, lif);
14765381Smeem 	if (lif->lif_packet_id == -1) {
14775381Smeem 		errmsg = "cannot register to receive DHCP packets";
14785381Smeem 		goto failure;
14795381Smeem 	}
14805381Smeem 
14810Sstevel@tonic-gate 	return (B_TRUE);
14825381Smeem failure:
14835381Smeem 	dhcpmsg(MSG_ERR, "open_ip_lif: %s: %s", lif->lif_name, errmsg);
14845381Smeem 	close_ip_lif(lif);
14855381Smeem 	return (B_FALSE);
14860Sstevel@tonic-gate }
14870Sstevel@tonic-gate 
14880Sstevel@tonic-gate /*
14893431Scarlsonj  * close_ip_lif(): close an IP socket for I/O on a given LIF.
14900Sstevel@tonic-gate  *
14913431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
14923431Scarlsonj  *  output: none
14930Sstevel@tonic-gate  */
14940Sstevel@tonic-gate 
14953431Scarlsonj void
close_ip_lif(dhcp_lif_t * lif)14963431Scarlsonj close_ip_lif(dhcp_lif_t *lif)
14970Sstevel@tonic-gate {
14985381Smeem 	if (lif->lif_packet_id != -1) {
14995381Smeem 		(void) iu_unregister_event(eh, lif->lif_packet_id, NULL);
15005381Smeem 		lif->lif_packet_id = -1;
15013431Scarlsonj 	}
15023431Scarlsonj 	if (lif->lif_sock_ip_fd != -1) {
15033431Scarlsonj 		(void) close(lif->lif_sock_ip_fd);
15043431Scarlsonj 		lif->lif_sock_ip_fd = -1;
15053431Scarlsonj 	}
15063431Scarlsonj }
15070Sstevel@tonic-gate 
15083431Scarlsonj /*
15093431Scarlsonj  * lif_mark_decline(): mark a LIF as having been declined due to a duplicate
15103431Scarlsonj  *		       address or some other conflict.  This is used in
15113431Scarlsonj  *		       send_declines() to report failure back to the server.
15123431Scarlsonj  *
15133431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
15143431Scarlsonj  *	    const char *: text string explaining why the address is declined
15153431Scarlsonj  *  output: none
15163431Scarlsonj  */
15170Sstevel@tonic-gate 
15183431Scarlsonj void
lif_mark_decline(dhcp_lif_t * lif,const char * reason)15193431Scarlsonj lif_mark_decline(dhcp_lif_t *lif, const char *reason)
15203431Scarlsonj {
15213431Scarlsonj 	if (lif->lif_declined == NULL) {
15223431Scarlsonj 		dhcp_lease_t *dlp;
15230Sstevel@tonic-gate 
15243431Scarlsonj 		lif->lif_declined = reason;
15253431Scarlsonj 		if ((dlp = lif->lif_lease) != NULL)
15263431Scarlsonj 			dlp->dl_smach->dsm_lif_down++;
15270Sstevel@tonic-gate 	}
15280Sstevel@tonic-gate }
15290Sstevel@tonic-gate 
15300Sstevel@tonic-gate /*
15313431Scarlsonj  * schedule_lif_timer(): schedules the LIF-related timer
15320Sstevel@tonic-gate  *
15333431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
15343431Scarlsonj  *	    dhcp_timer_t *: the timer to schedule
15353431Scarlsonj  *	    iu_tq_callback_t *: the callback to call upon firing
15363431Scarlsonj  *  output: boolean_t: B_TRUE if the timer was scheduled successfully
15370Sstevel@tonic-gate  */
15380Sstevel@tonic-gate 
15393431Scarlsonj boolean_t
schedule_lif_timer(dhcp_lif_t * lif,dhcp_timer_t * dt,iu_tq_callback_t * expire)15403431Scarlsonj schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire)
15410Sstevel@tonic-gate {
15420Sstevel@tonic-gate 	/*
15433431Scarlsonj 	 * If there's a timer running, cancel it and release its lease
15443431Scarlsonj 	 * reference.
15450Sstevel@tonic-gate 	 */
15463431Scarlsonj 	if (dt->dt_id != -1) {
15473431Scarlsonj 		if (!cancel_timer(dt))
15483431Scarlsonj 			return (B_FALSE);
15493431Scarlsonj 		release_lif(lif);
15503431Scarlsonj 	}
15510Sstevel@tonic-gate 
15523431Scarlsonj 	if (schedule_timer(dt, expire, lif)) {
15533431Scarlsonj 		hold_lif(lif);
15543431Scarlsonj 		return (B_TRUE);
15553431Scarlsonj 	} else {
15563431Scarlsonj 		dhcpmsg(MSG_WARNING,
15573431Scarlsonj 		    "schedule_lif_timer: cannot schedule timer");
15583431Scarlsonj 		return (B_FALSE);
15593431Scarlsonj 	}
15600Sstevel@tonic-gate }
15610Sstevel@tonic-gate 
15620Sstevel@tonic-gate /*
15633431Scarlsonj  * cancel_lif_timer(): cancels a LIF-related timer
15640Sstevel@tonic-gate  *
15653431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
15663431Scarlsonj  *	    dhcp_timer_t *: the timer to cancel
15673431Scarlsonj  *  output: none
15680Sstevel@tonic-gate  */
15690Sstevel@tonic-gate 
15703431Scarlsonj static void
cancel_lif_timer(dhcp_lif_t * lif,dhcp_timer_t * dt)15713431Scarlsonj cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt)
15720Sstevel@tonic-gate {
15733431Scarlsonj 	if (dt->dt_id == -1)
15743431Scarlsonj 		return;
15753431Scarlsonj 	if (cancel_timer(dt)) {
15763431Scarlsonj 		dhcpmsg(MSG_DEBUG2,
15773431Scarlsonj 		    "cancel_lif_timer: canceled expiry timer on %s",
15783431Scarlsonj 		    lif->lif_name);
15793431Scarlsonj 		release_lif(lif);
15803431Scarlsonj 	} else {
15813431Scarlsonj 		dhcpmsg(MSG_WARNING,
15823431Scarlsonj 		    "cancel_lif_timer: cannot cancel timer on %s",
15833431Scarlsonj 		    lif->lif_name);
15840Sstevel@tonic-gate 	}
15850Sstevel@tonic-gate }
15860Sstevel@tonic-gate 
15870Sstevel@tonic-gate /*
15883431Scarlsonj  * cancel_lif_timers(): cancels the LIF-related timers
15890Sstevel@tonic-gate  *
15903431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
15913431Scarlsonj  *  output: none
15920Sstevel@tonic-gate  */
15930Sstevel@tonic-gate 
15940Sstevel@tonic-gate void
cancel_lif_timers(dhcp_lif_t * lif)15953431Scarlsonj cancel_lif_timers(dhcp_lif_t *lif)
15960Sstevel@tonic-gate {
15973431Scarlsonj 	cancel_lif_timer(lif, &lif->lif_preferred);
15983431Scarlsonj 	cancel_lif_timer(lif, &lif->lif_expire);
15990Sstevel@tonic-gate }
16000Sstevel@tonic-gate 
16010Sstevel@tonic-gate /*
16023431Scarlsonj  * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common
16033431Scarlsonj  *		  file descriptors (v4_sock_fd and v6_sock_fd).
16040Sstevel@tonic-gate  *
16053431Scarlsonj  *   input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4
16063431Scarlsonj  *  output: none
16070Sstevel@tonic-gate  */
16080Sstevel@tonic-gate 
16093431Scarlsonj uint_t
get_max_mtu(boolean_t isv6)16103431Scarlsonj get_max_mtu(boolean_t isv6)
16110Sstevel@tonic-gate {
16123431Scarlsonj 	uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu;
16133431Scarlsonj 
16143431Scarlsonj 	if (*mtup == 0) {
16153431Scarlsonj 		dhcp_pif_t *pif;
16163431Scarlsonj 		dhcp_lif_t *lif;
16173431Scarlsonj 		struct lifreq lifr;
16183431Scarlsonj 
16193431Scarlsonj 		/* Set an arbitrary lower bound */
16203431Scarlsonj 		*mtup = 1024;
16213431Scarlsonj 		pif = isv6 ? v6root : v4root;
16223431Scarlsonj 		for (; pif != NULL; pif = pif->pif_next) {
16233431Scarlsonj 			for (lif = pif->pif_lifs; lif != NULL;
16243431Scarlsonj 			    lif = lif->lif_next) {
16253431Scarlsonj 				(void) strlcpy(lifr.lifr_name, lif->lif_name,
16263431Scarlsonj 				    LIFNAMSIZ);
16273431Scarlsonj 				if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) !=
16283431Scarlsonj 				    -1 && lifr.lifr_mtu > *mtup) {
16293431Scarlsonj 					*mtup = lifr.lifr_mtu;
16303431Scarlsonj 				}
16313431Scarlsonj 			}
16323431Scarlsonj 		}
16330Sstevel@tonic-gate 	}
16343431Scarlsonj 	return (*mtup);
16350Sstevel@tonic-gate }
16360Sstevel@tonic-gate 
16370Sstevel@tonic-gate /*
16383431Scarlsonj  * expired_lif_state(): summarize the state of expired LIFs on a given state
16393431Scarlsonj  *			machine.
16400Sstevel@tonic-gate  *
16413431Scarlsonj  *   input: dhcp_smach_t *: the state machine to scan
16423431Scarlsonj  *  output: dhcp_expire_t: overall state
16430Sstevel@tonic-gate  */
16440Sstevel@tonic-gate 
16453431Scarlsonj dhcp_expire_t
expired_lif_state(dhcp_smach_t * dsmp)16463431Scarlsonj expired_lif_state(dhcp_smach_t *dsmp)
16470Sstevel@tonic-gate {
16483431Scarlsonj 	dhcp_lease_t *dlp;
16493431Scarlsonj 	dhcp_lif_t *lif;
16503431Scarlsonj 	uint_t nlifs;
16513431Scarlsonj 	uint_t numlifs;
16523431Scarlsonj 	uint_t numexp;
16530Sstevel@tonic-gate 
16543431Scarlsonj 	numlifs = numexp = 0;
16553431Scarlsonj 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
16563431Scarlsonj 		lif = dlp->dl_lifs;
16573431Scarlsonj 		nlifs = dlp->dl_nlifs;
16583431Scarlsonj 		numlifs += nlifs;
16593431Scarlsonj 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
16603431Scarlsonj 			if (lif->lif_expired)
16613431Scarlsonj 				numexp++;
16623431Scarlsonj 		}
16630Sstevel@tonic-gate 	}
16643431Scarlsonj 	if (numlifs == 0)
16653431Scarlsonj 		return (DHCP_EXP_NOLIFS);
16663431Scarlsonj 	else if (numexp == 0)
16673431Scarlsonj 		return (DHCP_EXP_NOEXP);
16683431Scarlsonj 	else if (numlifs == numexp)
16693431Scarlsonj 		return (DHCP_EXP_ALLEXP);
16703431Scarlsonj 	else
16713431Scarlsonj 		return (DHCP_EXP_SOMEEXP);
16720Sstevel@tonic-gate }
16730Sstevel@tonic-gate 
16740Sstevel@tonic-gate /*
16753431Scarlsonj  * find_expired_lif(): find the first expired LIF on a given state machine
16760Sstevel@tonic-gate  *
16773431Scarlsonj  *   input: dhcp_smach_t *: the state machine to scan
16783431Scarlsonj  *  output: dhcp_lif_t *: the first expired LIF, or NULL if none.
16790Sstevel@tonic-gate  */
16800Sstevel@tonic-gate 
16813431Scarlsonj dhcp_lif_t *
find_expired_lif(dhcp_smach_t * dsmp)16823431Scarlsonj find_expired_lif(dhcp_smach_t *dsmp)
16830Sstevel@tonic-gate {
16843431Scarlsonj 	dhcp_lease_t *dlp;
16853431Scarlsonj 	dhcp_lif_t *lif;
16863431Scarlsonj 	uint_t nlifs;
16873431Scarlsonj 
16883431Scarlsonj 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
16893431Scarlsonj 		lif = dlp->dl_lifs;
16903431Scarlsonj 		nlifs = dlp->dl_nlifs;
16913431Scarlsonj 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
16923431Scarlsonj 			if (lif->lif_expired)
16933431Scarlsonj 				return (lif);
16943431Scarlsonj 		}
16953431Scarlsonj 	}
16963431Scarlsonj 	return (NULL);
16973431Scarlsonj }
16983431Scarlsonj 
16993431Scarlsonj /*
17003431Scarlsonj  * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING.  Used
17013431Scarlsonj  *		       only for DHCPv6.
17023431Scarlsonj  *
17033431Scarlsonj  *   input: none
17043431Scarlsonj  *  output: none
17053431Scarlsonj  */
17063431Scarlsonj 
17073431Scarlsonj void
remove_v6_strays(void)17083431Scarlsonj remove_v6_strays(void)
17093431Scarlsonj {
17103431Scarlsonj 	struct lifnum lifn;
17113431Scarlsonj 	struct lifconf lifc;
17123431Scarlsonj 	struct lifreq *lifrp, *lifrmax;
17133431Scarlsonj 	uint_t numifs;
17143431Scarlsonj 	uint64_t flags;
17150Sstevel@tonic-gate 
17160Sstevel@tonic-gate 	/*
17173431Scarlsonj 	 * Get the approximate number of interfaces in the system.  It's only
17183431Scarlsonj 	 * approximate because the system is dynamic -- interfaces may be
17193431Scarlsonj 	 * plumbed or unplumbed at any time.  This is also the reason for the
17203431Scarlsonj 	 * "+ 10" fudge factor: we're trying to avoid unnecessary looping.
17210Sstevel@tonic-gate 	 */
17223431Scarlsonj 	(void) memset(&lifn, 0, sizeof (lifn));
17233431Scarlsonj 	lifn.lifn_family = AF_INET6;
17243431Scarlsonj 	lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
17253431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) {
17263431Scarlsonj 		dhcpmsg(MSG_ERR,
17273431Scarlsonj 		    "remove_v6_strays: cannot read number of interfaces");
17283431Scarlsonj 		numifs = 10;
17293431Scarlsonj 	} else {
17303431Scarlsonj 		numifs = lifn.lifn_count + 10;
17310Sstevel@tonic-gate 	}
17320Sstevel@tonic-gate 
17330Sstevel@tonic-gate 	/*
17343431Scarlsonj 	 * Get the interface information.  We do this in a loop so that we can
17353431Scarlsonj 	 * recover from EINVAL from the kernel -- delivered when the buffer is
17363431Scarlsonj 	 * too small.
17370Sstevel@tonic-gate 	 */
17383431Scarlsonj 	(void) memset(&lifc, 0, sizeof (lifc));
17393431Scarlsonj 	lifc.lifc_family = AF_INET6;
17403431Scarlsonj 	lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
17413431Scarlsonj 	for (;;) {
17423431Scarlsonj 		lifc.lifc_len = numifs * sizeof (*lifrp);
17433431Scarlsonj 		lifrp = realloc(lifc.lifc_buf, lifc.lifc_len);
17443431Scarlsonj 		if (lifrp == NULL) {
17453431Scarlsonj 			dhcpmsg(MSG_ERR,
17463431Scarlsonj 			    "remove_v6_strays: cannot allocate memory");
17473431Scarlsonj 			free(lifc.lifc_buf);
17483431Scarlsonj 			return;
17490Sstevel@tonic-gate 		}
17503431Scarlsonj 		lifc.lifc_buf = (caddr_t)lifrp;
17513431Scarlsonj 		errno = 0;
17523431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 &&
17533431Scarlsonj 		    lifc.lifc_len < numifs * sizeof (*lifrp))
17540Sstevel@tonic-gate 			break;
17553431Scarlsonj 		if (errno == 0 || errno == EINVAL) {
17563431Scarlsonj 			numifs <<= 1;
17573431Scarlsonj 		} else {
17583431Scarlsonj 			dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF");
17593431Scarlsonj 			free(lifc.lifc_buf);
17603431Scarlsonj 			return;
17610Sstevel@tonic-gate 		}
17620Sstevel@tonic-gate 	}
17630Sstevel@tonic-gate 
17643431Scarlsonj 	lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp);
17653431Scarlsonj 	for (; lifrp < lifrmax; lifrp++) {
17660Sstevel@tonic-gate 		/*
17673431Scarlsonj 		 * Get the interface flags; we're interested in the DHCP ones.
17680Sstevel@tonic-gate 		 */
17693431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1)
17703431Scarlsonj 			continue;
17713431Scarlsonj 		flags = lifrp->lifr_flags;
17723431Scarlsonj 		if (!(flags & IFF_DHCPRUNNING))
17733431Scarlsonj 			continue;
17740Sstevel@tonic-gate 		/*
17753431Scarlsonj 		 * If the interface has a link-local address, then we don't
17763431Scarlsonj 		 * control it.  Just remove the flag.
17770Sstevel@tonic-gate 		 */
17783431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1)
17793431Scarlsonj 			continue;
17803431Scarlsonj 		if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp->
17813431Scarlsonj 		    lifr_addr)->sin6_addr)) {
17823431Scarlsonj 			lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING;
17833431Scarlsonj 			(void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp);
17843431Scarlsonj 			continue;
17850Sstevel@tonic-gate 		}
17860Sstevel@tonic-gate 		/*
17873431Scarlsonj 		 * All others are (or were) under our control.  Clean up by
17883431Scarlsonj 		 * removing them.
17890Sstevel@tonic-gate 		 */
17903431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) {
17913431Scarlsonj 			dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s",
17923431Scarlsonj 			    lifrp->lifr_name);
17933431Scarlsonj 		} else if (errno != ENXIO) {
17943431Scarlsonj 			dhcpmsg(MSG_ERR,
17953431Scarlsonj 			    "remove_v6_strays: SIOCLIFREMOVEIF %s",
17963431Scarlsonj 			    lifrp->lifr_name);
17970Sstevel@tonic-gate 		}
17980Sstevel@tonic-gate 	}
17993431Scarlsonj 	free(lifc.lifc_buf);
18000Sstevel@tonic-gate }
1801