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 /*
22*3431Scarlsonj  * 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 <sys/dlpi.h>
320Sstevel@tonic-gate #include <stdlib.h>
330Sstevel@tonic-gate #include <sys/sockio.h>
340Sstevel@tonic-gate #include <netinet/in.h>
350Sstevel@tonic-gate #include <netinet/dhcp.h>
360Sstevel@tonic-gate #include <string.h>
370Sstevel@tonic-gate #include <unistd.h>
38*3431Scarlsonj #include <search.h>
39*3431Scarlsonj #include <libdevinfo.h>
400Sstevel@tonic-gate #include <netinet/if_ether.h>
41*3431Scarlsonj #include <arpa/inet.h>
420Sstevel@tonic-gate #include <dhcpmsg.h>
43*3431Scarlsonj #include <dhcp_inittab.h>
440Sstevel@tonic-gate 
45*3431Scarlsonj #include "agent.h"
460Sstevel@tonic-gate #include "interface.h"
470Sstevel@tonic-gate #include "util.h"
480Sstevel@tonic-gate #include "dlpi_io.h"
490Sstevel@tonic-gate #include "packet.h"
500Sstevel@tonic-gate #include "states.h"
51*3431Scarlsonj 
52*3431Scarlsonj dhcp_pif_t *v4root;
53*3431Scarlsonj dhcp_pif_t *v6root;
54*3431Scarlsonj 
55*3431Scarlsonj static uint_t cached_v4_max_mtu, cached_v6_max_mtu;
560Sstevel@tonic-gate 
570Sstevel@tonic-gate /*
58*3431Scarlsonj  * Interface flags to watch: things that should be under our direct control.
590Sstevel@tonic-gate  */
60*3431Scarlsonj #define	DHCP_IFF_WATCH	(IFF_DHCPRUNNING | IFF_DEPRECATED | \
61*3431Scarlsonj 	IFF_ADDRCONF | IFF_MIPRUNNING | IFF_TEMPORARY)
620Sstevel@tonic-gate 
63*3431Scarlsonj static void clear_lif_dhcp(dhcp_lif_t *);
640Sstevel@tonic-gate 
650Sstevel@tonic-gate /*
66*3431Scarlsonj  * insert_pif(): creates a new physical interface structure and chains it on
67*3431Scarlsonj  *		 the list.  Initializes state that remains consistent across
68*3431Scarlsonj  *		 all use of the physical interface entry.
690Sstevel@tonic-gate  *
70*3431Scarlsonj  *   input: const char *: the name of the physical interface
71*3431Scarlsonj  *	    boolean_t: if B_TRUE, this is DHCPv6
72*3431Scarlsonj  *	    int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
730Sstevel@tonic-gate  *		   error code with the reason why
74*3431Scarlsonj  *  output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure
750Sstevel@tonic-gate  */
760Sstevel@tonic-gate 
77*3431Scarlsonj dhcp_pif_t *
78*3431Scarlsonj insert_pif(const char *pname, boolean_t isv6, int *error)
790Sstevel@tonic-gate {
80*3431Scarlsonj 	dhcp_pif_t *pif;
81*3431Scarlsonj 	struct lifreq lifr;
820Sstevel@tonic-gate 
83*3431Scarlsonj 	if ((pif = calloc(1, sizeof (*pif))) == NULL) {
84*3431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_pif: cannot allocate pif entry for "
85*3431Scarlsonj 		    "%s", pname);
86*3431Scarlsonj 		*error = DHCP_IPC_E_MEMORY;
870Sstevel@tonic-gate 		return (NULL);
880Sstevel@tonic-gate 	}
890Sstevel@tonic-gate 
90*3431Scarlsonj 	pif->pif_isv6 = isv6;
91*3431Scarlsonj 	pif->pif_dlpi_fd = -1;
92*3431Scarlsonj 	pif->pif_dlpi_id = -1;
93*3431Scarlsonj 	pif->pif_hold_count = 1;
94*3431Scarlsonj 	pif->pif_running = B_TRUE;
95*3431Scarlsonj 
96*3431Scarlsonj 	if (strlcpy(pif->pif_name, pname, LIFNAMSIZ) >= LIFNAMSIZ) {
97*3431Scarlsonj 		dhcpmsg(MSG_ERROR, "insert_pif: interface name %s is too long",
98*3431Scarlsonj 		    pname);
99*3431Scarlsonj 		*error = DHCP_IPC_E_INVIF;
100*3431Scarlsonj 		goto failure;
101*3431Scarlsonj 	}
102*3431Scarlsonj 
103*3431Scarlsonj 	/* We do not use DLPI with DHCPv6 */
104*3431Scarlsonj 	if (!isv6) {
105*3431Scarlsonj 		uint32_t		buf[DLPI_BUF_MAX / sizeof (uint32_t)];
106*3431Scarlsonj 		dl_info_ack_t		*dlia = (dl_info_ack_t *)buf;
107*3431Scarlsonj 		caddr_t			dl_addr;
108*3431Scarlsonj 
109*3431Scarlsonj 		/*
110*3431Scarlsonj 		 * Do the allocations necessary for IPv4 DHCP.
111*3431Scarlsonj 		 *
112*3431Scarlsonj 		 *  1. open the interface using DLPI
113*3431Scarlsonj 		 *  2. get the interface max SDU
114*3431Scarlsonj 		 *  3. get the interface hardware type and hardware length
115*3431Scarlsonj 		 *  4. get the interface hardware address
116*3431Scarlsonj 		 *  5. get the interface hardware broadcast address
117*3431Scarlsonj 		 */
118*3431Scarlsonj 
119*3431Scarlsonj 		/* step 1 */
120*3431Scarlsonj 		pif->pif_dlpi_fd = dlpi_open(pname, dlia, sizeof (buf),
121*3431Scarlsonj 		    ETHERTYPE_IP);
122*3431Scarlsonj 		if (pif->pif_dlpi_fd == -1) {
123*3431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
124*3431Scarlsonj 			goto failure;
125*3431Scarlsonj 		}
126*3431Scarlsonj 
127*3431Scarlsonj 		/* step 2 */
128*3431Scarlsonj 		pif->pif_max = dlia->dl_max_sdu;
129*3431Scarlsonj 		if (pif->pif_max < DHCP_DEF_MAX_SIZE) {
130*3431Scarlsonj 			dhcpmsg(MSG_ERROR, "insert_pif: %s does not have a "
131*3431Scarlsonj 			    "large enough maximum SDU to support DHCP "
132*3431Scarlsonj 			    "(%u < %u)", pname, pif->pif_max,
133*3431Scarlsonj 			    DHCP_DEF_MAX_SIZE);
134*3431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
135*3431Scarlsonj 			goto failure;
136*3431Scarlsonj 		}
137*3431Scarlsonj 
138*3431Scarlsonj 		/* step 3 */
139*3431Scarlsonj 		pif->pif_hwtype = dlpi_to_arp(dlia->dl_mac_type);
140*3431Scarlsonj 		pif->pif_hwlen  = dlia->dl_addr_length -
141*3431Scarlsonj 		    abs(dlia->dl_sap_length);
142*3431Scarlsonj 
143*3431Scarlsonj 		dhcpmsg(MSG_DEBUG, "insert_pif: %s: sdumax %u, hwtype %d, "
144*3431Scarlsonj 		    "hwlen %d", pname, pif->pif_max, pif->pif_hwtype,
145*3431Scarlsonj 		    pif->pif_hwlen);
146*3431Scarlsonj 
147*3431Scarlsonj 		/* step 4 */
148*3431Scarlsonj 		if (pif->pif_hwlen > 0) {
149*3431Scarlsonj 			pif->pif_hwaddr = malloc(pif->pif_hwlen);
150*3431Scarlsonj 			if (pif->pif_hwaddr == NULL) {
151*3431Scarlsonj 				dhcpmsg(MSG_ERR, "insert_pif: cannot allocate "
152*3431Scarlsonj 				    "pif_hwaddr for %s", pname);
153*3431Scarlsonj 				*error = DHCP_IPC_E_MEMORY;
154*3431Scarlsonj 				goto failure;
155*3431Scarlsonj 			}
156*3431Scarlsonj 		}
157*3431Scarlsonj 
158*3431Scarlsonj 		/*
159*3431Scarlsonj 		 * depending on the DLPI device, the sap and hardware addresses
160*3431Scarlsonj 		 * can be in either order within the dlsap address; find the
161*3431Scarlsonj 		 * location of the hardware address using dl_sap_length.  see
162*3431Scarlsonj 		 * the DLPI specification for more on this braindamage.
163*3431Scarlsonj 		 */
164*3431Scarlsonj 
165*3431Scarlsonj 		dl_addr = (caddr_t)dlia + dlia->dl_addr_offset;
166*3431Scarlsonj 		if (dlia->dl_sap_length > 0) {
167*3431Scarlsonj 			pif->pif_sap_before = B_TRUE;
168*3431Scarlsonj 			dl_addr += dlia->dl_sap_length;
169*3431Scarlsonj 		}
170*3431Scarlsonj 
171*3431Scarlsonj 		(void) memcpy(pif->pif_hwaddr, dl_addr, pif->pif_hwlen);
172*3431Scarlsonj 
173*3431Scarlsonj 		/* step 5 */
174*3431Scarlsonj 		pif->pif_saplen = abs(dlia->dl_sap_length);
175*3431Scarlsonj 		pif->pif_daddr  = build_broadcast_dest(dlia, &pif->pif_dlen);
176*3431Scarlsonj 		if (pif->pif_daddr == NULL) {
177*3431Scarlsonj 			dhcpmsg(MSG_ERR, "insert_pif: cannot allocate "
178*3431Scarlsonj 			    "pif_daddr for %s", pname);
179*3431Scarlsonj 			*error = DHCP_IPC_E_MEMORY;
180*3431Scarlsonj 			goto failure;
181*3431Scarlsonj 		}
182*3431Scarlsonj 
183*3431Scarlsonj 		/* Close the DLPI stream until actually needed */
184*3431Scarlsonj 		close_dlpi_pif(pif);
185*3431Scarlsonj 	}
186*3431Scarlsonj 
1870Sstevel@tonic-gate 	/*
188*3431Scarlsonj 	 * This is a bit gross, but IP has a confused interface.  We must
189*3431Scarlsonj 	 * assume that the zeroth LIF is plumbed, and must query there to get
190*3431Scarlsonj 	 * the interface index number.
1910Sstevel@tonic-gate 	 */
192*3431Scarlsonj 	(void) strlcpy(lifr.lifr_name, pname, LIFNAMSIZ);
1930Sstevel@tonic-gate 
194*3431Scarlsonj 	if (ioctl(isv6 ? v6_sock_fd : v4_sock_fd, SIOCGLIFINDEX, &lifr) == -1) {
195*3431Scarlsonj 		if (errno == ENXIO)
196*3431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
197*3431Scarlsonj 		else
198*3431Scarlsonj 			*error = DHCP_IPC_E_INT;
199*3431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX for %s", pname);
200*3431Scarlsonj 		goto failure;
201*3431Scarlsonj 	}
202*3431Scarlsonj 	pif->pif_index = lifr.lifr_index;
203*3431Scarlsonj 
204*3431Scarlsonj 	insque(pif, isv6 ? &v6root : &v4root);
205*3431Scarlsonj 
206*3431Scarlsonj 	return (pif);
207*3431Scarlsonj 
208*3431Scarlsonj failure:
209*3431Scarlsonj 	release_pif(pif);
210*3431Scarlsonj 	return (NULL);
211*3431Scarlsonj }
212*3431Scarlsonj 
213*3431Scarlsonj /*
214*3431Scarlsonj  * hold_pif(): acquire a hold on a physical interface structure.
215*3431Scarlsonj  *
216*3431Scarlsonj  *   input: dhcp_pif_t *: a pointer to the PIF structure
217*3431Scarlsonj  *  output: none
218*3431Scarlsonj  */
219*3431Scarlsonj 
220*3431Scarlsonj void
221*3431Scarlsonj hold_pif(dhcp_pif_t *pif)
222*3431Scarlsonj {
223*3431Scarlsonj 	pif->pif_hold_count++;
224*3431Scarlsonj 	dhcpmsg(MSG_DEBUG2, "hold_pif: hold count on %s: %u", pif->pif_name,
225*3431Scarlsonj 	    pif->pif_hold_count);
226*3431Scarlsonj }
227*3431Scarlsonj 
228*3431Scarlsonj /*
229*3431Scarlsonj  * release_pif(): release a hold on a physical interface structure; will
230*3431Scarlsonj  *		  destroy the structure on the last hold removed.
231*3431Scarlsonj  *
232*3431Scarlsonj  *   input: dhcp_pif_t *: a pointer to the PIF structure
233*3431Scarlsonj  *  output: none
234*3431Scarlsonj  */
235*3431Scarlsonj 
236*3431Scarlsonj void
237*3431Scarlsonj release_pif(dhcp_pif_t *pif)
238*3431Scarlsonj {
239*3431Scarlsonj 	if (pif->pif_hold_count == 0) {
240*3431Scarlsonj 		dhcpmsg(MSG_CRIT, "release_pif: extraneous release");
241*3431Scarlsonj 		return;
242*3431Scarlsonj 	}
243*3431Scarlsonj 
244*3431Scarlsonj 	if (--pif->pif_hold_count == 0) {
245*3431Scarlsonj 		dhcpmsg(MSG_DEBUG, "release_pif: freeing PIF %s",
246*3431Scarlsonj 		    pif->pif_name);
247*3431Scarlsonj 
248*3431Scarlsonj 		remque(pif);
249*3431Scarlsonj 		pif->pif_dlpi_count = 1;
250*3431Scarlsonj 		close_dlpi_pif(pif);
251*3431Scarlsonj 		free(pif->pif_hwaddr);
252*3431Scarlsonj 		free(pif->pif_daddr);
253*3431Scarlsonj 		free(pif);
254*3431Scarlsonj 	} else {
255*3431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "release_pif: hold count on %s: %u",
256*3431Scarlsonj 		    pif->pif_name, pif->pif_hold_count);
257*3431Scarlsonj 	}
258*3431Scarlsonj }
259*3431Scarlsonj 
260*3431Scarlsonj /*
261*3431Scarlsonj  * lookup_pif_by_index(): Looks up PIF entries given regular ifIndex.
262*3431Scarlsonj  *
263*3431Scarlsonj  *   input: uint_t: the interface index
264*3431Scarlsonj  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
265*3431Scarlsonj  *  output: dhcp_pif_t *: the matching PIF, or NULL if not found
266*3431Scarlsonj  */
267*3431Scarlsonj 
268*3431Scarlsonj dhcp_pif_t *
269*3431Scarlsonj lookup_pif_by_index(uint_t ifindex, boolean_t isv6)
270*3431Scarlsonj {
271*3431Scarlsonj 	dhcp_pif_t *pif;
272*3431Scarlsonj 
273*3431Scarlsonj 	for (pif = isv6 ? v6root : v4root; pif != NULL; pif = pif->pif_next) {
274*3431Scarlsonj 		if (pif->pif_index == ifindex)
275*3431Scarlsonj 			break;
276*3431Scarlsonj 	}
277*3431Scarlsonj 
278*3431Scarlsonj 	return (pif);
279*3431Scarlsonj }
280*3431Scarlsonj 
281*3431Scarlsonj /*
282*3431Scarlsonj  * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and
283*3431Scarlsonj  *			   previous PIF pointer (or NULL for list start).
284*3431Scarlsonj  *			   Caller is expected to iterate through all
285*3431Scarlsonj  *			   potential matches to find interface of interest.
286*3431Scarlsonj  *
287*3431Scarlsonj  *   input: uint16_t: the interface index (truncated)
288*3431Scarlsonj  *	    dhcp_pif_t *: the previous PIF, or NULL for list start
289*3431Scarlsonj  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
290*3431Scarlsonj  *  output: dhcp_pif_t *: the next matching PIF, or NULL if not found
291*3431Scarlsonj  *    note: This operates using the 'truncated' (16-bit) ifindex as seen by
292*3431Scarlsonj  *	    routing socket clients.  The value stored in pif_index is the
293*3431Scarlsonj  *	    32-bit ifindex from the ioctl interface.
294*3431Scarlsonj  */
295*3431Scarlsonj 
296*3431Scarlsonj dhcp_pif_t *
297*3431Scarlsonj lookup_pif_by_uindex(uint16_t ifindex, dhcp_pif_t *pif, boolean_t isv6)
298*3431Scarlsonj {
299*3431Scarlsonj 	if (pif == NULL)
300*3431Scarlsonj 		pif = isv6 ? v6root : v4root;
301*3431Scarlsonj 	else
302*3431Scarlsonj 		pif = pif->pif_next;
303*3431Scarlsonj 
304*3431Scarlsonj 	for (; pif != NULL; pif = pif->pif_next) {
305*3431Scarlsonj 		if ((pif->pif_index & 0xffff) == ifindex)
306*3431Scarlsonj 			break;
307*3431Scarlsonj 	}
308*3431Scarlsonj 
309*3431Scarlsonj 	return (pif);
310*3431Scarlsonj }
311*3431Scarlsonj 
312*3431Scarlsonj /*
313*3431Scarlsonj  * lookup_pif_by_name(): Looks up a physical interface entry given a name.
314*3431Scarlsonj  *
315*3431Scarlsonj  *   input: const char *: the physical interface name
316*3431Scarlsonj  *	    boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
317*3431Scarlsonj  *  output: dhcp_pif_t *: the matching PIF, or NULL if not found
318*3431Scarlsonj  */
319*3431Scarlsonj 
320*3431Scarlsonj dhcp_pif_t *
321*3431Scarlsonj lookup_pif_by_name(const char *pname, boolean_t isv6)
322*3431Scarlsonj {
323*3431Scarlsonj 	dhcp_pif_t *pif;
324*3431Scarlsonj 
325*3431Scarlsonj 	pif = isv6 ? v6root : v4root;
326*3431Scarlsonj 
327*3431Scarlsonj 	for (; pif != NULL; pif = pif->pif_next) {
328*3431Scarlsonj 		if (strcmp(pif->pif_name, pname) == 0)
329*3431Scarlsonj 			break;
330*3431Scarlsonj 	}
331*3431Scarlsonj 
332*3431Scarlsonj 	return (pif);
333*3431Scarlsonj }
334*3431Scarlsonj 
335*3431Scarlsonj /*
336*3431Scarlsonj  * open_dlpi_pif(): register the use of DLPI I/O by a LIF on a PIF, opening
337*3431Scarlsonj  *		    the connection if necessary.
338*3431Scarlsonj  *
339*3431Scarlsonj  *   input: dhcp_pif_t *: the physical interface on which to use DLPI
340*3431Scarlsonj  *  output: boolean_t: B_TRUE on success, B_FALSE on failure.
341*3431Scarlsonj  */
342*3431Scarlsonj 
343*3431Scarlsonj boolean_t
344*3431Scarlsonj open_dlpi_pif(dhcp_pif_t *pif)
345*3431Scarlsonj {
346*3431Scarlsonj 	if (pif->pif_dlpi_fd == -1) {
347*3431Scarlsonj 		uint32_t		buf[DLPI_BUF_MAX / sizeof (uint32_t)];
348*3431Scarlsonj 		dl_info_ack_t		*dlia = (dl_info_ack_t *)buf;
349*3431Scarlsonj 
350*3431Scarlsonj 		pif->pif_dlpi_fd = dlpi_open(pif->pif_name, dlia, sizeof (buf),
351*3431Scarlsonj 		    ETHERTYPE_IP);
352*3431Scarlsonj 		if (pif->pif_dlpi_fd == -1)
353*3431Scarlsonj 			return (B_FALSE);
354*3431Scarlsonj 		set_packet_filter(pif->pif_dlpi_fd, dhcp_filter, NULL, "DHCP");
355*3431Scarlsonj 		pif->pif_dlpi_id = iu_register_event(eh, pif->pif_dlpi_fd,
356*3431Scarlsonj 		    POLLIN, dhcp_collect_dlpi, pif);
357*3431Scarlsonj 		if (pif->pif_dlpi_id == -1) {
358*3431Scarlsonj 			(void) dlpi_close(pif->pif_dlpi_fd);
359*3431Scarlsonj 			pif->pif_dlpi_fd = -1;
360*3431Scarlsonj 			return (B_FALSE);
361*3431Scarlsonj 		}
362*3431Scarlsonj 	}
363*3431Scarlsonj 	pif->pif_dlpi_count++;
364*3431Scarlsonj 	return (B_TRUE);
365*3431Scarlsonj }
366*3431Scarlsonj 
367*3431Scarlsonj /*
368*3431Scarlsonj  * close_dlpi_pif(): unregister the use of DLPI I/O by a LIF on a PIF, closing
369*3431Scarlsonj  *		     the connection if this was the last user.
370*3431Scarlsonj  *
371*3431Scarlsonj  *   input: dhcp_pif_t *: the physical interface on which we're using DLPI
372*3431Scarlsonj  *  output: none
373*3431Scarlsonj  */
374*3431Scarlsonj 
375*3431Scarlsonj void
376*3431Scarlsonj close_dlpi_pif(dhcp_pif_t *pif)
377*3431Scarlsonj {
378*3431Scarlsonj 	if (pif->pif_dlpi_count > 1) {
379*3431Scarlsonj 		pif->pif_dlpi_count--;
380*3431Scarlsonj 		return;
381*3431Scarlsonj 	}
382*3431Scarlsonj 	pif->pif_dlpi_count = 0;
383*3431Scarlsonj 	if (pif->pif_dlpi_id != -1) {
384*3431Scarlsonj 		(void) iu_unregister_event(eh, pif->pif_dlpi_id, NULL);
385*3431Scarlsonj 		pif->pif_dlpi_id = -1;
386*3431Scarlsonj 	}
387*3431Scarlsonj 	if (pif->pif_dlpi_fd != -1) {
388*3431Scarlsonj 		(void) dlpi_close(pif->pif_dlpi_fd);
389*3431Scarlsonj 		pif->pif_dlpi_fd = -1;
390*3431Scarlsonj 	}
391*3431Scarlsonj }
392*3431Scarlsonj 
393*3431Scarlsonj /*
394*3431Scarlsonj  * pif_status(): update the physical interface up/down status.
395*3431Scarlsonj  *
396*3431Scarlsonj  *   input: dhcp_pif_t *: the physical interface on which we're using DLPI
397*3431Scarlsonj  *	    boolean_t: B_TRUE if the interface is going up
398*3431Scarlsonj  *  output: none
399*3431Scarlsonj  */
400*3431Scarlsonj 
401*3431Scarlsonj void
402*3431Scarlsonj pif_status(dhcp_pif_t *pif, boolean_t isup)
403*3431Scarlsonj {
404*3431Scarlsonj 	dhcp_lif_t *lif;
405*3431Scarlsonj 	dhcp_smach_t *dsmp;
406*3431Scarlsonj 
407*3431Scarlsonj 	pif->pif_running = isup;
408*3431Scarlsonj 	dhcpmsg(LOG_DEBUG, "interface %s has %s", pif->pif_name,
409*3431Scarlsonj 	    isup ? "come back up" : "gone down");
410*3431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
411*3431Scarlsonj 		for (dsmp = lif->lif_smachs; dsmp != NULL;
412*3431Scarlsonj 		    dsmp = dsmp->dsm_next) {
413*3431Scarlsonj 			if (isup)
414*3431Scarlsonj 				refresh_smach(dsmp);
415*3431Scarlsonj 			else
416*3431Scarlsonj 				remove_default_routes(dsmp);
417*3431Scarlsonj 		}
418*3431Scarlsonj 	}
419*3431Scarlsonj }
420*3431Scarlsonj 
421*3431Scarlsonj /* Helper for insert_lif: extract addresses as defined */
422*3431Scarlsonj #define	ASSIGN_ADDR(v4, v6, lf) \
423*3431Scarlsonj 	if (pif->pif_isv6) { \
424*3431Scarlsonj 		lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \
425*3431Scarlsonj 	} else { \
426*3431Scarlsonj 		lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \
427*3431Scarlsonj 	}
428*3431Scarlsonj 
429*3431Scarlsonj /*
430*3431Scarlsonj  * insert_lif(): Creates a new logical interface structure and chains it on
431*3431Scarlsonj  *		 the list for a given physical interface.  Initializes state
432*3431Scarlsonj  *		 that remains consistent across all use of the logical
433*3431Scarlsonj  *		 interface entry.  Caller's PIF hold is transferred to the
434*3431Scarlsonj  *		 LIF on success, and is dropped on failure.
435*3431Scarlsonj  *
436*3431Scarlsonj  *   input: dhcp_pif_t *: pointer to the physical interface for this LIF
437*3431Scarlsonj  *	    const char *: the name of the logical interface
438*3431Scarlsonj  *	    int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
439*3431Scarlsonj  *		   error code with the reason why
440*3431Scarlsonj  *  output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure
441*3431Scarlsonj  */
442*3431Scarlsonj 
443*3431Scarlsonj dhcp_lif_t *
444*3431Scarlsonj insert_lif(dhcp_pif_t *pif, const char *lname, int *error)
445*3431Scarlsonj {
446*3431Scarlsonj 	dhcp_lif_t *lif;
447*3431Scarlsonj 	int fd;
448*3431Scarlsonj 	struct lifreq lifr;
449*3431Scarlsonj 
450*3431Scarlsonj 	if ((lif = calloc(1, sizeof (*lif))) == NULL) {
451*3431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: cannot allocate lif entry for "
452*3431Scarlsonj 		    "%s", lname);
4530Sstevel@tonic-gate 		*error = DHCP_IPC_E_MEMORY;
4540Sstevel@tonic-gate 		return (NULL);
4550Sstevel@tonic-gate 	}
4560Sstevel@tonic-gate 
457*3431Scarlsonj 	lif->lif_sock_ip_fd = -1;
458*3431Scarlsonj 	lif->lif_acknak_id = -1;
459*3431Scarlsonj 	lif->lif_iaid_id = -1;
460*3431Scarlsonj 	lif->lif_hold_count = 1;
461*3431Scarlsonj 	lif->lif_pif = pif;
462*3431Scarlsonj 	lif->lif_removed = B_TRUE;
463*3431Scarlsonj 	init_timer(&lif->lif_preferred, 0);
464*3431Scarlsonj 	init_timer(&lif->lif_expire, 0);
4650Sstevel@tonic-gate 
466*3431Scarlsonj 	if (strlcpy(lif->lif_name, lname, LIFNAMSIZ) >= LIFNAMSIZ) {
467*3431Scarlsonj 		dhcpmsg(MSG_ERROR, "insert_lif: interface name %s is too long",
468*3431Scarlsonj 		    lname);
4690Sstevel@tonic-gate 		*error = DHCP_IPC_E_INVIF;
4700Sstevel@tonic-gate 		goto failure;
4710Sstevel@tonic-gate 	}
4720Sstevel@tonic-gate 
473*3431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lname, LIFNAMSIZ);
474*3431Scarlsonj 
475*3431Scarlsonj 	fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
4760Sstevel@tonic-gate 
477*3431Scarlsonj 	if (ioctl(fd, SIOCGLIFMTU, &lifr) == -1)
478*3431Scarlsonj 		lif->lif_max = 1024;
479*3431Scarlsonj 	else
480*3431Scarlsonj 		lif->lif_max = lifr.lifr_mtu;
4810Sstevel@tonic-gate 
482*3431Scarlsonj 	if (ioctl(fd, SIOCGLIFADDR, &lifr) == -1) {
483*3431Scarlsonj 		if (errno == ENXIO)
484*3431Scarlsonj 			*error = DHCP_IPC_E_INVIF;
485*3431Scarlsonj 		else
486*3431Scarlsonj 			*error = DHCP_IPC_E_INT;
487*3431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFADDR for %s", lname);
4880Sstevel@tonic-gate 		goto failure;
4890Sstevel@tonic-gate 	}
490*3431Scarlsonj 	ASSIGN_ADDR(lif_addr, lif_v6addr, lifr_addr);
4910Sstevel@tonic-gate 
492*3431Scarlsonj 	if (ioctl(fd, SIOCGLIFNETMASK, &lifr) == -1) {
4932546Scarlsonj 		if (errno == ENXIO)
4942546Scarlsonj 			*error = DHCP_IPC_E_INVIF;
4952546Scarlsonj 		else
4962546Scarlsonj 			*error = DHCP_IPC_E_INT;
497*3431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFNETMASK for %s", lname);
498*3431Scarlsonj 		goto failure;
499*3431Scarlsonj 	}
500*3431Scarlsonj 	ASSIGN_ADDR(lif_netmask, lif_v6mask, lifr_addr);
501*3431Scarlsonj 
502*3431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
503*3431Scarlsonj 		*error = DHCP_IPC_E_INT;
504*3431Scarlsonj 		dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFFLAGS for %s", lname);
5052546Scarlsonj 		goto failure;
5062546Scarlsonj 	}
507*3431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
508*3431Scarlsonj 
509*3431Scarlsonj 	/*
510*3431Scarlsonj 	 * If we've just detected the interface going up or down, then signal
511*3431Scarlsonj 	 * an appropriate action.  There may be other state machines here.
512*3431Scarlsonj 	 */
513*3431Scarlsonj 	if ((lifr.lifr_flags & IFF_RUNNING) && !pif->pif_running) {
514*3431Scarlsonj 		pif_status(pif, B_TRUE);
515*3431Scarlsonj 	} else if (!(lifr.lifr_flags & IFF_RUNNING) && pif->pif_running) {
516*3431Scarlsonj 		pif_status(pif, B_FALSE);
517*3431Scarlsonj 	}
518*3431Scarlsonj 
519*3431Scarlsonj 	if (lifr.lifr_flags & IFF_POINTOPOINT) {
520*3431Scarlsonj 		if (ioctl(fd, SIOCGLIFDSTADDR, &lifr) == -1) {
521*3431Scarlsonj 			*error = DHCP_IPC_E_INT;
522*3431Scarlsonj 			dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFDSTADDR for %s",
523*3431Scarlsonj 			    lname);
524*3431Scarlsonj 			goto failure;
525*3431Scarlsonj 		}
526*3431Scarlsonj 		ASSIGN_ADDR(lif_peer, lif_v6peer, lifr_dstaddr);
527*3431Scarlsonj 	} else if (!pif->pif_isv6 && (lifr.lifr_flags & IFF_BROADCAST)) {
528*3431Scarlsonj 		if (ioctl(fd, SIOCGLIFBRDADDR, &lifr) == -1) {
529*3431Scarlsonj 			*error = DHCP_IPC_E_INT;
530*3431Scarlsonj 			dhcpmsg(MSG_ERR, "insert_lif: SIOCGLIFBRDADDR for %s",
531*3431Scarlsonj 			    lname);
532*3431Scarlsonj 			goto failure;
533*3431Scarlsonj 		}
534*3431Scarlsonj 		lif->lif_broadcast =
535*3431Scarlsonj 		    ((struct sockaddr_in *)&lifr.lifr_broadaddr)->sin_addr.
536*3431Scarlsonj 		    s_addr;
537*3431Scarlsonj 	}
538*3431Scarlsonj 
539*3431Scarlsonj 	if (pif->pif_isv6)
540*3431Scarlsonj 		cached_v6_max_mtu = 0;
541*3431Scarlsonj 	else
542*3431Scarlsonj 		cached_v4_max_mtu = 0;
543*3431Scarlsonj 
544*3431Scarlsonj 	lif->lif_removed = B_FALSE;
545*3431Scarlsonj 	insque(lif, &pif->pif_lifs);
546*3431Scarlsonj 
547*3431Scarlsonj 	return (lif);
548*3431Scarlsonj 
549*3431Scarlsonj failure:
550*3431Scarlsonj 	release_lif(lif);
551*3431Scarlsonj 	return (NULL);
552*3431Scarlsonj }
553*3431Scarlsonj 
554*3431Scarlsonj /*
555*3431Scarlsonj  * hold_lif(): acquire a hold on a logical interface structure.
556*3431Scarlsonj  *
557*3431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
558*3431Scarlsonj  *  output: none
559*3431Scarlsonj  */
560*3431Scarlsonj 
561*3431Scarlsonj void
562*3431Scarlsonj hold_lif(dhcp_lif_t *lif)
563*3431Scarlsonj {
564*3431Scarlsonj 	lif->lif_hold_count++;
565*3431Scarlsonj 	dhcpmsg(MSG_DEBUG2, "hold_lif: hold count on %s: %u", lif->lif_name,
566*3431Scarlsonj 	    lif->lif_hold_count);
567*3431Scarlsonj }
568*3431Scarlsonj 
569*3431Scarlsonj /*
570*3431Scarlsonj  * release_lif(): release a hold on a logical interface structure; will
571*3431Scarlsonj  *		  destroy the structure on the last hold removed.
572*3431Scarlsonj  *
573*3431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
574*3431Scarlsonj  *  output: none
575*3431Scarlsonj  */
576*3431Scarlsonj 
577*3431Scarlsonj void
578*3431Scarlsonj release_lif(dhcp_lif_t *lif)
579*3431Scarlsonj {
580*3431Scarlsonj 	if (lif->lif_hold_count == 0) {
581*3431Scarlsonj 		dhcpmsg(MSG_CRIT, "release_lif: extraneous release on %s",
582*3431Scarlsonj 		    lif->lif_name);
583*3431Scarlsonj 		return;
584*3431Scarlsonj 	}
585*3431Scarlsonj 
586*3431Scarlsonj 	if (lif->lif_hold_count == 1 && !lif->lif_removed) {
587*3431Scarlsonj 		unplumb_lif(lif);
588*3431Scarlsonj 		return;
589*3431Scarlsonj 	}
590*3431Scarlsonj 
591*3431Scarlsonj 	if (--lif->lif_hold_count == 0) {
592*3431Scarlsonj 		dhcp_pif_t *pif;
593*3431Scarlsonj 
594*3431Scarlsonj 		dhcpmsg(MSG_DEBUG, "release_lif: freeing LIF %s",
595*3431Scarlsonj 		    lif->lif_name);
596*3431Scarlsonj 
597*3431Scarlsonj 		if (lif->lif_lease != NULL)
598*3431Scarlsonj 			dhcpmsg(MSG_CRIT,
599*3431Scarlsonj 			    "release_lif: still holding lease at last hold!");
600*3431Scarlsonj 		close_ip_lif(lif);
601*3431Scarlsonj 		pif = lif->lif_pif;
602*3431Scarlsonj 		if (pif->pif_isv6)
603*3431Scarlsonj 			cached_v6_max_mtu = 0;
604*3431Scarlsonj 		else
605*3431Scarlsonj 			cached_v4_max_mtu = 0;
606*3431Scarlsonj 		release_pif(pif);
607*3431Scarlsonj 		free(lif);
608*3431Scarlsonj 	} else {
609*3431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "release_lif: hold count on %s: %u",
610*3431Scarlsonj 		    lif->lif_name, lif->lif_hold_count);
611*3431Scarlsonj 	}
612*3431Scarlsonj }
613*3431Scarlsonj 
614*3431Scarlsonj /*
615*3431Scarlsonj  * remove_lif(): remove a logical interface from its PIF and lease (if any) and
616*3431Scarlsonj  *		 the lease's hold on the LIF.  Assumes that we did not plumb
617*3431Scarlsonj  *		 the interface.
618*3431Scarlsonj  *
619*3431Scarlsonj  *   input: dhcp_lif_t *: a pointer to the LIF structure
620*3431Scarlsonj  *  output: none
621*3431Scarlsonj  */
622*3431Scarlsonj 
623*3431Scarlsonj void
624*3431Scarlsonj remove_lif(dhcp_lif_t *lif)
625*3431Scarlsonj {
626*3431Scarlsonj 	if (lif->lif_plumbed) {
627*3431Scarlsonj 		dhcpmsg(MSG_CRIT, "remove_lif: attempted invalid removal of %s",
628*3431Scarlsonj 		    lif->lif_name);
629*3431Scarlsonj 		return;
630*3431Scarlsonj 	}
631*3431Scarlsonj 	if (lif->lif_removed) {
632*3431Scarlsonj 		dhcpmsg(MSG_CRIT, "remove_lif: extraneous removal of %s",
633*3431Scarlsonj 		    lif->lif_name);
634*3431Scarlsonj 	} else {
635*3431Scarlsonj 		dhcp_lif_t *lifnext;
636*3431Scarlsonj 		dhcp_lease_t *dlp;
637*3431Scarlsonj 
638*3431Scarlsonj 		dhcpmsg(MSG_DEBUG2, "remove_lif: removing %s", lif->lif_name);
639*3431Scarlsonj 		lif->lif_removed = B_TRUE;
640*3431Scarlsonj 		lifnext = lif->lif_next;
641*3431Scarlsonj 		clear_lif_dhcp(lif);
642*3431Scarlsonj 		cancel_lif_timers(lif);
643*3431Scarlsonj 		if (lif->lif_iaid_id != -1 &&
644*3431Scarlsonj 		    iu_cancel_timer(tq, lif->lif_iaid_id, NULL) == 1) {
645*3431Scarlsonj 			lif->lif_iaid_id = -1;
646*3431Scarlsonj 			release_lif(lif);
647*3431Scarlsonj 		}
648*3431Scarlsonj 
649*3431Scarlsonj 		/* Remove from PIF list */
650*3431Scarlsonj 		remque(lif);
651*3431Scarlsonj 
652*3431Scarlsonj 		/* If we were part of a lease, then remove ourselves */
653*3431Scarlsonj 		if ((dlp = lif->lif_lease) != NULL) {
654*3431Scarlsonj 			if (--dlp->dl_nlifs == 0)
655*3431Scarlsonj 				dlp->dl_lifs = NULL;
656*3431Scarlsonj 			else if (dlp->dl_lifs == lif)
657*3431Scarlsonj 				dlp->dl_lifs = lifnext;
658*3431Scarlsonj 			if (lif->lif_flags & IFF_DHCPRUNNING)
659*3431Scarlsonj 				clear_lif_dhcp(lif);
660*3431Scarlsonj 			if (lif->lif_declined != NULL) {
661*3431Scarlsonj 				dlp->dl_smach->dsm_lif_down--;
662*3431Scarlsonj 				lif->lif_declined = NULL;
663*3431Scarlsonj 			}
664*3431Scarlsonj 			lif->lif_lease = NULL;
665*3431Scarlsonj 			release_lif(lif);
666*3431Scarlsonj 		}
667*3431Scarlsonj 	}
668*3431Scarlsonj }
669*3431Scarlsonj 
670*3431Scarlsonj /*
671*3431Scarlsonj  * lookup_lif_by_name(): Looks up a logical interface entry given a name and
672*3431Scarlsonj  *			 a physical interface.
673*3431Scarlsonj  *
674*3431Scarlsonj  *   input: const char *: the logical interface name
675*3431Scarlsonj  *	    const dhcp_pif_t *: the physical interface
676*3431Scarlsonj  *  output: dhcp_lif_t *: the matching LIF, or NULL if not found
677*3431Scarlsonj  */
678*3431Scarlsonj 
679*3431Scarlsonj dhcp_lif_t *
680*3431Scarlsonj lookup_lif_by_name(const char *lname, const dhcp_pif_t *pif)
681*3431Scarlsonj {
682*3431Scarlsonj 	dhcp_lif_t *lif;
683*3431Scarlsonj 
684*3431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
685*3431Scarlsonj 		if (strcmp(lif->lif_name, lname) == 0)
686*3431Scarlsonj 			break;
687*3431Scarlsonj 	}
688*3431Scarlsonj 
689*3431Scarlsonj 	return (lif);
690*3431Scarlsonj }
691*3431Scarlsonj 
692*3431Scarlsonj /*
693*3431Scarlsonj  * checkaddr(): checks if the given address is still set on the given LIF
694*3431Scarlsonj  *
695*3431Scarlsonj  *   input: const dhcp_lif_t *: the LIF to check
696*3431Scarlsonj  *	    int: the address to look up on the interface (ioctl)
697*3431Scarlsonj  *	    const in6_addr_t *: the address to compare to
698*3431Scarlsonj  *	    const char *: name of the address for logging purposes
699*3431Scarlsonj  *  output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
700*3431Scarlsonj  */
701*3431Scarlsonj 
702*3431Scarlsonj static boolean_t
703*3431Scarlsonj checkaddr(const dhcp_lif_t *lif, int ioccmd, const in6_addr_t *addr,
704*3431Scarlsonj     const char *aname)
705*3431Scarlsonj {
706*3431Scarlsonj 	boolean_t isv6;
707*3431Scarlsonj 	int fd;
708*3431Scarlsonj 	struct lifreq lifr;
709*3431Scarlsonj 
710*3431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
711*3431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
712*3431Scarlsonj 
713*3431Scarlsonj 	isv6 = lif->lif_pif->pif_isv6;
714*3431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
715*3431Scarlsonj 
716*3431Scarlsonj 	if (ioctl(fd, ioccmd, &lifr) == -1) {
717*3431Scarlsonj 		if (errno == ENXIO) {
718*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "checkaddr: interface %s is gone",
719*3431Scarlsonj 			    lif->lif_name);
720*3431Scarlsonj 			return (B_FALSE);
721*3431Scarlsonj 		}
722*3431Scarlsonj 		dhcpmsg(MSG_DEBUG,
723*3431Scarlsonj 		    "checkaddr: ignoring ioctl error on %s %x: %s",
724*3431Scarlsonj 		    lif->lif_name, ioccmd, strerror(errno));
725*3431Scarlsonj 	} else if (isv6) {
726*3431Scarlsonj 		struct sockaddr_in6 *sin6 =
727*3431Scarlsonj 		    (struct sockaddr_in6 *)&lifr.lifr_addr;
728*3431Scarlsonj 		char abuf1[INET6_ADDRSTRLEN];
729*3431Scarlsonj 		char abuf2[INET6_ADDRSTRLEN];
730*3431Scarlsonj 
731*3431Scarlsonj 		if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, addr)) {
732*3431Scarlsonj 			dhcpmsg(MSG_WARNING,
733*3431Scarlsonj 			    "checkaddr: expected %s %s on %s, have %s",
734*3431Scarlsonj 			    aname, inet_ntop(AF_INET6, &sin6->sin6_addr, abuf1,
735*3431Scarlsonj 			    sizeof (abuf1)),  lif->lif_name,
736*3431Scarlsonj 			    inet_ntop(AF_INET6, addr, abuf2, sizeof (abuf2)));
737*3431Scarlsonj 			return (B_FALSE);
738*3431Scarlsonj 		}
739*3431Scarlsonj 	} else {
740*3431Scarlsonj 		struct sockaddr_in *sinp =
741*3431Scarlsonj 		    (struct sockaddr_in *)&lifr.lifr_addr;
742*3431Scarlsonj 		ipaddr_t v4addr;
743*3431Scarlsonj 		char abuf1[INET_ADDRSTRLEN];
744*3431Scarlsonj 		char abuf2[INET_ADDRSTRLEN];
745*3431Scarlsonj 
746*3431Scarlsonj 		IN6_V4MAPPED_TO_IPADDR(addr, v4addr);
747*3431Scarlsonj 		if (sinp->sin_addr.s_addr != v4addr) {
748*3431Scarlsonj 			dhcpmsg(MSG_WARNING,
749*3431Scarlsonj 			    "checkaddr: expected %s %s on %s, have %s",
750*3431Scarlsonj 			    aname, inet_ntop(AF_INET, &sinp->sin_addr, abuf1,
751*3431Scarlsonj 			    sizeof (abuf1)),  lif->lif_name,
752*3431Scarlsonj 			    inet_ntop(AF_INET, &v4addr, abuf2,
753*3431Scarlsonj 			    sizeof (abuf2)));
754*3431Scarlsonj 			return (B_FALSE);
755*3431Scarlsonj 		}
756*3431Scarlsonj 	}
757*3431Scarlsonj 	return (B_TRUE);
758*3431Scarlsonj }
759*3431Scarlsonj 
760*3431Scarlsonj /*
761*3431Scarlsonj  * verify_lif(): verifies than a LIF is still valid (i.e., has not been
762*3431Scarlsonj  *		 explicitly or implicitly dropped or released)
763*3431Scarlsonj  *
764*3431Scarlsonj  *   input: const dhcp_lif_t *: the LIF to verify
765*3431Scarlsonj  *  output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise
766*3431Scarlsonj  */
767*3431Scarlsonj 
768*3431Scarlsonj boolean_t
769*3431Scarlsonj verify_lif(const dhcp_lif_t *lif)
770*3431Scarlsonj {
771*3431Scarlsonj 	boolean_t isv6;
772*3431Scarlsonj 	int fd;
773*3431Scarlsonj 	struct lifreq lifr;
774*3431Scarlsonj 
775*3431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
776*3431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
777*3431Scarlsonj 
778*3431Scarlsonj 	isv6 = lif->lif_pif->pif_isv6;
779*3431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
780*3431Scarlsonj 
781*3431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
782*3431Scarlsonj 		dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFFLAGS failed on %s",
783*3431Scarlsonj 		    lif->lif_name);
784*3431Scarlsonj 		return (B_FALSE);
785*3431Scarlsonj 	}
786*3431Scarlsonj 
787*3431Scarlsonj 	/*
788*3431Scarlsonj 	 * If important flags have changed, then abandon the interface.
789*3431Scarlsonj 	 */
790*3431Scarlsonj 	if ((lif->lif_flags ^ lifr.lifr_flags) & DHCP_IFF_WATCH) {
791*3431Scarlsonj 		dhcpmsg(MSG_DEBUG, "verify_lif: unexpected flag change on %s: "
792*3431Scarlsonj 		    "%llx to %llx (%llx)", lif->lif_name, lif->lif_flags,
793*3431Scarlsonj 		    lifr.lifr_flags, (lif->lif_flags ^ lifr.lifr_flags) &
794*3431Scarlsonj 		    DHCP_IFF_WATCH);
795*3431Scarlsonj 		return (B_FALSE);
796*3431Scarlsonj 	}
797*3431Scarlsonj 
798*3431Scarlsonj 	/*
799*3431Scarlsonj 	 * Special case: if the interface has gone down as a duplicate, then
800*3431Scarlsonj 	 * this alone does _not_ mean that we're abandoning it just yet.  Allow
801*3431Scarlsonj 	 * the state machine to handle this normally by trying to get a new
802*3431Scarlsonj 	 * lease.
803*3431Scarlsonj 	 */
804*3431Scarlsonj 	if ((lifr.lifr_flags & (IFF_UP|IFF_DUPLICATE)) == IFF_DUPLICATE) {
805*3431Scarlsonj 		dhcpmsg(MSG_DEBUG, "verify_lif: duplicate address on %s",
806*3431Scarlsonj 		    lif->lif_name);
807*3431Scarlsonj 		return (B_TRUE);
808*3431Scarlsonj 	}
809*3431Scarlsonj 
810*3431Scarlsonj 	/*
811*3431Scarlsonj 	 * If the user has torn down or started up the interface manually, then
812*3431Scarlsonj 	 * abandon the lease.
813*3431Scarlsonj 	 */
814*3431Scarlsonj 	if ((lif->lif_flags ^ lifr.lifr_flags) & IFF_UP) {
815*3431Scarlsonj 		dhcpmsg(MSG_DEBUG, "verify_lif: user has %s %s",
816*3431Scarlsonj 		    lifr.lifr_flags & IFF_UP ? "started up" : "shut down",
817*3431Scarlsonj 		    lif->lif_name);
818*3431Scarlsonj 		return (B_FALSE);
819*3431Scarlsonj 	}
820*3431Scarlsonj 
821*3431Scarlsonj 	/*
822*3431Scarlsonj 	 * Check for delete and recreate.
823*3431Scarlsonj 	 */
824*3431Scarlsonj 	if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
825*3431Scarlsonj 		dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed on %s",
826*3431Scarlsonj 		    lif->lif_name);
827*3431Scarlsonj 		return (B_FALSE);
828*3431Scarlsonj 	}
829*3431Scarlsonj 	if (lifr.lifr_index != lif->lif_pif->pif_index) {
830*3431Scarlsonj 		dhcpmsg(MSG_DEBUG,
831*3431Scarlsonj 		    "verify_lif: ifindex on %s changed: %u to %u",
832*3431Scarlsonj 		    lif->lif_name, lif->lif_pif->pif_index, lifr.lifr_index);
833*3431Scarlsonj 		return (B_FALSE);
834*3431Scarlsonj 	}
835*3431Scarlsonj 
836*3431Scarlsonj 	/*
837*3431Scarlsonj 	 * If the IP address, netmask, or broadcast address have changed, or
838*3431Scarlsonj 	 * the interface has been unplumbed, then we act like there has been an
839*3431Scarlsonj 	 * implicit drop.  (Note that the netmask is under DHCP control for
840*3431Scarlsonj 	 * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast
841*3431Scarlsonj 	 * addresses.)
842*3431Scarlsonj 	 */
8432546Scarlsonj 
844*3431Scarlsonj 	if (!checkaddr(lif, SIOCGLIFADDR, &lif->lif_v6addr, "local address"))
845*3431Scarlsonj 		return (B_FALSE);
846*3431Scarlsonj 
847*3431Scarlsonj 	if (isv6) {
848*3431Scarlsonj 		/*
849*3431Scarlsonj 		 * If it's not point-to-point, we're done.  If it is, then
850*3431Scarlsonj 		 * check the peer's address as well.
851*3431Scarlsonj 		 */
852*3431Scarlsonj 		return (!(lif->lif_flags & IFF_POINTOPOINT) ||
853*3431Scarlsonj 		    checkaddr(lif, SIOCGLIFDSTADDR, &lif->lif_v6peer,
854*3431Scarlsonj 		    "peer address"));
855*3431Scarlsonj 	} else {
856*3431Scarlsonj 		if (!checkaddr(lif, SIOCGLIFNETMASK, &lif->lif_v6mask,
857*3431Scarlsonj 		    "netmask"))
858*3431Scarlsonj 			return (B_FALSE);
859*3431Scarlsonj 
860*3431Scarlsonj 		return (checkaddr(lif,
861*3431Scarlsonj 		    (lif->lif_flags & IFF_POINTOPOINT) ? SIOCGLIFDSTADDR :
862*3431Scarlsonj 		    SIOCGLIFBRDADDR, &lif->lif_v6peer, "peer address"));
863*3431Scarlsonj 	}
864*3431Scarlsonj }
865*3431Scarlsonj 
866*3431Scarlsonj /*
867*3431Scarlsonj  * canonize_lif(): puts the interface in a canonical (zeroed) form.  This is
868*3431Scarlsonj  *		   used only on the "main" LIF for IPv4.  All other interfaces
869*3431Scarlsonj  *		   are under dhcpagent control and are removed using
870*3431Scarlsonj  *		   unplumb_lif().
871*3431Scarlsonj  *
872*3431Scarlsonj  *   input: dhcp_lif_t *: the interface to canonize
873*3431Scarlsonj  *  output: none
874*3431Scarlsonj  */
875*3431Scarlsonj 
876*3431Scarlsonj static void
877*3431Scarlsonj canonize_lif(dhcp_lif_t *lif)
878*3431Scarlsonj {
879*3431Scarlsonj 	boolean_t isv6;
880*3431Scarlsonj 	int fd;
881*3431Scarlsonj 	struct lifreq lifr;
882*3431Scarlsonj 
883*3431Scarlsonj 	/*
884*3431Scarlsonj 	 * If there's nothing here, then don't touch the interface.  This can
885*3431Scarlsonj 	 * happen when an already-canonized LIF is recanonized.
886*3431Scarlsonj 	 */
887*3431Scarlsonj 	if (IN6_IS_ADDR_UNSPECIFIED(&lif->lif_v6addr))
888*3431Scarlsonj 		return;
889*3431Scarlsonj 
890*3431Scarlsonj 	isv6 = lif->lif_pif->pif_isv6;
891*3431Scarlsonj 	dhcpmsg(MSG_VERBOSE, "canonizing IPv%d interface %s",
892*3431Scarlsonj 	    isv6 ? 6 : 4, lif->lif_name);
893*3431Scarlsonj 
894*3431Scarlsonj 	lif->lif_v6addr = my_in6addr_any;
895*3431Scarlsonj 	lif->lif_v6mask = my_in6addr_any;
896*3431Scarlsonj 	lif->lif_v6peer = my_in6addr_any;
897*3431Scarlsonj 
898*3431Scarlsonj 	(void) memset(&lifr, 0, sizeof (struct lifreq));
899*3431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
900*3431Scarlsonj 
901*3431Scarlsonj 	fd = isv6 ? v6_sock_fd : v4_sock_fd;
902*3431Scarlsonj 
903*3431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
904*3431Scarlsonj 		dhcpmsg(MSG_ERR, "canonize_lif: can't get flags for %s",
905*3431Scarlsonj 		    lif->lif_name);
906*3431Scarlsonj 		return;
907*3431Scarlsonj 	}
908*3431Scarlsonj 
909*3431Scarlsonj 	/* Should not happen */
910*3431Scarlsonj 	if (!(lifr.lifr_flags & IFF_DHCPRUNNING)) {
911*3431Scarlsonj 		dhcpmsg(MSG_INFO,
912*3431Scarlsonj 		    "canonize_lif: cannot clear %s; flags are %llx",
913*3431Scarlsonj 		    lif->lif_name, lifr.lifr_flags);
914*3431Scarlsonj 		return;
915*3431Scarlsonj 	}
916*3431Scarlsonj 
917*3431Scarlsonj 	/*
918*3431Scarlsonj 	 * clear the UP flag, but don't clear DHCPRUNNING since
919*3431Scarlsonj 	 * that should only be done when the interface is removed
920*3431Scarlsonj 	 * (see clear_lif_dhcp() and remove_lif())
921*3431Scarlsonj 	 */
922*3431Scarlsonj 
923*3431Scarlsonj 	lif->lif_flags = lifr.lifr_flags &= ~IFF_UP;
924*3431Scarlsonj 
925*3431Scarlsonj 	if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
926*3431Scarlsonj 		dhcpmsg(MSG_ERR, "canonize_lif: can't set flags for %s",
927*3431Scarlsonj 		    lif->lif_name);
928*3431Scarlsonj 		return;
929*3431Scarlsonj 	}
930*3431Scarlsonj 
931*3431Scarlsonj 	(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
932*3431Scarlsonj 	if (isv6) {
933*3431Scarlsonj 		struct sockaddr_in6 *sin6 =
934*3431Scarlsonj 		    (struct sockaddr_in6 *)&lifr.lifr_addr;
935*3431Scarlsonj 
936*3431Scarlsonj 		sin6->sin6_family = AF_INET6;
937*3431Scarlsonj 		sin6->sin6_addr = my_in6addr_any;
938*3431Scarlsonj 	} else {
939*3431Scarlsonj 		struct sockaddr_in *sinv =
940*3431Scarlsonj 		    (struct sockaddr_in *)&lifr.lifr_addr;
941*3431Scarlsonj 
942*3431Scarlsonj 		sinv->sin_family = AF_INET;
943*3431Scarlsonj 		sinv->sin_addr.s_addr = htonl(INADDR_ANY);
944*3431Scarlsonj 	}
945*3431Scarlsonj 
946*3431Scarlsonj 	if (ioctl(fd, SIOCSLIFADDR, &lifr) == -1) {
947*3431Scarlsonj 		dhcpmsg(MSG_ERR,
948*3431Scarlsonj 		    "canonize_lif: can't clear local address on %s",
949*3431Scarlsonj 		    lif->lif_name);
950*3431Scarlsonj 	}
951*3431Scarlsonj 
952*3431Scarlsonj 	/* Netmask is under in.ndpd control with IPv6 */
953*3431Scarlsonj 	if (!isv6 && ioctl(fd, SIOCSLIFNETMASK, &lifr) == -1) {
954*3431Scarlsonj 		dhcpmsg(MSG_ERR, "canonize_lif: can't clear netmask on %s",
955*3431Scarlsonj 		    lif->lif_name);
956*3431Scarlsonj 	}
957*3431Scarlsonj 
958*3431Scarlsonj 	if (lif->lif_flags & IFF_POINTOPOINT) {
959*3431Scarlsonj 		if (ioctl(fd, SIOCSLIFDSTADDR, &lifr) == -1) {
960*3431Scarlsonj 			dhcpmsg(MSG_ERR,
961*3431Scarlsonj 			    "canonize_lif: can't clear remote address on %s",
962*3431Scarlsonj 			    lif->lif_name);
963*3431Scarlsonj 		}
964*3431Scarlsonj 	} else if (!isv6) {
965*3431Scarlsonj 		if (ioctl(fd, SIOCSLIFBRDADDR, &lifr) == -1) {
966*3431Scarlsonj 			dhcpmsg(MSG_ERR,
967*3431Scarlsonj 			    "canonize_lif: can't clear broadcast address on %s",
968*3431Scarlsonj 			    lif->lif_name);
969*3431Scarlsonj 		}
970*3431Scarlsonj 	}
971*3431Scarlsonj }
972*3431Scarlsonj 
973*3431Scarlsonj /*
974*3431Scarlsonj  * plumb_lif(): Adds the LIF to the system.  This is used for all
975*3431Scarlsonj  *		DHCPv6-derived interfaces.  The returned LIF has a hold
976*3431Scarlsonj  *		on it.
977*3431Scarlsonj  *
978*3431Scarlsonj  *   input: dhcp_lif_t *: the interface to unplumb
979*3431Scarlsonj  *  output: none
980*3431Scarlsonj  */
981*3431Scarlsonj 
982*3431Scarlsonj dhcp_lif_t *
983*3431Scarlsonj plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr)
984*3431Scarlsonj {
985*3431Scarlsonj 	dhcp_lif_t *lif;
986*3431Scarlsonj 	char abuf[INET6_ADDRSTRLEN];
987*3431Scarlsonj 	struct lifreq lifr;
988*3431Scarlsonj 	struct sockaddr_in6 *sin6;
989*3431Scarlsonj 	int error;
990*3431Scarlsonj 
991*3431Scarlsonj 	(void) inet_ntop(AF_INET6, addr, abuf, sizeof (abuf));
992*3431Scarlsonj 
993*3431Scarlsonj 	for (lif = pif->pif_lifs; lif != NULL; lif = lif->lif_next) {
994*3431Scarlsonj 		if (IN6_ARE_ADDR_EQUAL(&lif->lif_v6addr, addr)) {
995*3431Scarlsonj 			dhcpmsg(MSG_ERR,
996*3431Scarlsonj 			    "plumb_lif: entry for %s already exists!", abuf);
997*3431Scarlsonj 			return (NULL);
998*3431Scarlsonj 		}
999*3431Scarlsonj 	}
1000*3431Scarlsonj 
1001*3431Scarlsonj 	/* First, create a new zero-address logical interface */
1002*3431Scarlsonj 	(void) memset(&lifr, 0, sizeof (lifr));
1003*3431Scarlsonj 	(void) strlcpy(lifr.lifr_name, pif->pif_name, sizeof (lifr.lifr_name));
1004*3431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCLIFADDIF, &lifr) == -1) {
1005*3431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFADDIF %s", pif->pif_name);
1006*3431Scarlsonj 		return (NULL);
1007*3431Scarlsonj 	}
1008*3431Scarlsonj 
1009*3431Scarlsonj 	/* Next, set the netmask to all ones */
1010*3431Scarlsonj 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_addr;
1011*3431Scarlsonj 	sin6->sin6_family = AF_INET6;
1012*3431Scarlsonj 	(void) memset(&sin6->sin6_addr, 0xff, sizeof (sin6->sin6_addr));
1013*3431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFNETMASK, &lifr) == -1) {
1014*3431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFNETMASK %s",
1015*3431Scarlsonj 		    lifr.lifr_name);
10160Sstevel@tonic-gate 		goto failure;
10170Sstevel@tonic-gate 	}
10180Sstevel@tonic-gate 
1019*3431Scarlsonj 	/* Now set the interface address */
1020*3431Scarlsonj 	sin6->sin6_addr = *addr;
1021*3431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFADDR, &lifr) == -1) {
1022*3431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFADDR %s %s",
1023*3431Scarlsonj 		    lifr.lifr_name, abuf);
1024*3431Scarlsonj 		goto failure;
1025*3431Scarlsonj 	}
1026*3431Scarlsonj 
1027*3431Scarlsonj 	/* Mark the interface up */
1028*3431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
1029*3431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCGLIFFLAGS %s",
1030*3431Scarlsonj 		    lifr.lifr_name);
1031*3431Scarlsonj 		goto failure;
1032*3431Scarlsonj 	}
1033*3431Scarlsonj 	lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING;
1034*3431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
1035*3431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s",
1036*3431Scarlsonj 		    lifr.lifr_name);
1037*3431Scarlsonj 		goto failure;
1038*3431Scarlsonj 	}
1039*3431Scarlsonj 
1040*3431Scarlsonj 	/* Now we can create the internal LIF structure */
1041*3431Scarlsonj 	hold_pif(pif);
1042*3431Scarlsonj 	if ((lif = insert_lif(pif, lifr.lifr_name, &error)) == NULL)
1043*3431Scarlsonj 		goto failure;
1044*3431Scarlsonj 
1045*3431Scarlsonj 	dhcpmsg(MSG_DEBUG, "plumb_lif: plumbed up %s on %s", abuf,
1046*3431Scarlsonj 	    lif->lif_name);
1047*3431Scarlsonj 	lif->lif_plumbed = B_TRUE;
1048*3431Scarlsonj 
1049*3431Scarlsonj 	return (lif);
1050*3431Scarlsonj 
1051*3431Scarlsonj failure:
1052*3431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
1053*3431Scarlsonj 	    errno != ENXIO) {
1054*3431Scarlsonj 		dhcpmsg(MSG_ERR, "plumb_lif: SIOCLIFREMOVEIF %s",
1055*3431Scarlsonj 		    lifr.lifr_name);
1056*3431Scarlsonj 	}
1057*3431Scarlsonj 	return (NULL);
1058*3431Scarlsonj }
1059*3431Scarlsonj 
1060*3431Scarlsonj /*
1061*3431Scarlsonj  * unplumb_lif(): Removes the LIF from dhcpagent and the system.  This is used
1062*3431Scarlsonj  *		  for all interfaces configured by DHCP (those in leases).
1063*3431Scarlsonj  *
1064*3431Scarlsonj  *   input: dhcp_lif_t *: the interface to unplumb
1065*3431Scarlsonj  *  output: none
1066*3431Scarlsonj  */
1067*3431Scarlsonj 
1068*3431Scarlsonj void
1069*3431Scarlsonj unplumb_lif(dhcp_lif_t *lif)
1070*3431Scarlsonj {
1071*3431Scarlsonj 	dhcp_lease_t *dlp;
1072*3431Scarlsonj 
1073*3431Scarlsonj 	if (lif->lif_plumbed) {
1074*3431Scarlsonj 		struct lifreq lifr;
1075*3431Scarlsonj 
1076*3431Scarlsonj 		(void) memset(&lifr, 0, sizeof (lifr));
1077*3431Scarlsonj 		(void) strlcpy(lifr.lifr_name, lif->lif_name,
1078*3431Scarlsonj 		    sizeof (lifr.lifr_name));
1079*3431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, &lifr) == -1 &&
1080*3431Scarlsonj 		    errno != ENXIO) {
1081*3431Scarlsonj 			dhcpmsg(MSG_ERR, "unplumb_lif: SIOCLIFREMOVEIF %s",
1082*3431Scarlsonj 			    lif->lif_name);
1083*3431Scarlsonj 		}
1084*3431Scarlsonj 		lif->lif_plumbed = B_FALSE;
1085*3431Scarlsonj 	}
1086*3431Scarlsonj 	lif->lif_flags = 0;
1087*3431Scarlsonj 	/*
1088*3431Scarlsonj 	 * Special case: if we're "unplumbing" the main LIF for DHCPv4, then
1089*3431Scarlsonj 	 * just canonize it and remove it from the lease.
1090*3431Scarlsonj 	 */
1091*3431Scarlsonj 	if ((dlp = lif->lif_lease) != NULL && dlp->dl_smach->dsm_lif == lif) {
1092*3431Scarlsonj 		canonize_lif(lif);
1093*3431Scarlsonj 		cancel_lif_timers(lif);
1094*3431Scarlsonj 		if (lif->lif_declined != NULL) {
1095*3431Scarlsonj 			dlp->dl_smach->dsm_lif_down--;
1096*3431Scarlsonj 			lif->lif_declined = NULL;
1097*3431Scarlsonj 		}
1098*3431Scarlsonj 		dlp->dl_nlifs = 0;
1099*3431Scarlsonj 		dlp->dl_lifs = NULL;
1100*3431Scarlsonj 		lif->lif_lease = NULL;
1101*3431Scarlsonj 		release_lif(lif);
1102*3431Scarlsonj 	} else {
1103*3431Scarlsonj 		remove_lif(lif);
1104*3431Scarlsonj 	}
1105*3431Scarlsonj }
1106*3431Scarlsonj 
1107*3431Scarlsonj /*
1108*3431Scarlsonj  * attach_lif(): create a new logical interface, creating the physical
1109*3431Scarlsonj  *		 interface as necessary.
1110*3431Scarlsonj  *
1111*3431Scarlsonj  *   input: const char *: the logical interface name
1112*3431Scarlsonj  *	    boolean_t: B_TRUE for IPv6
1113*3431Scarlsonj  *	    int *: set to DHCP_IPC_E_* if creation fails
1114*3431Scarlsonj  *  output: dhcp_lif_t *: pointer to new entry, or NULL on failure
1115*3431Scarlsonj  */
1116*3431Scarlsonj 
1117*3431Scarlsonj dhcp_lif_t *
1118*3431Scarlsonj attach_lif(const char *lname, boolean_t isv6, int *error)
1119*3431Scarlsonj {
1120*3431Scarlsonj 	dhcp_pif_t *pif;
1121*3431Scarlsonj 	char pname[LIFNAMSIZ], *cp;
1122*3431Scarlsonj 
1123*3431Scarlsonj 	(void) strlcpy(pname, lname, sizeof (pname));
1124*3431Scarlsonj 	if ((cp = strchr(pname, ':')) != NULL)
1125*3431Scarlsonj 		*cp = '\0';
1126*3431Scarlsonj 
1127*3431Scarlsonj 	if ((pif = lookup_pif_by_name(pname, isv6)) != NULL)
1128*3431Scarlsonj 		hold_pif(pif);
1129*3431Scarlsonj 	else if ((pif = insert_pif(pname, isv6, error)) == NULL)
1130*3431Scarlsonj 		return (NULL);
1131*3431Scarlsonj 
1132*3431Scarlsonj 	if (lookup_lif_by_name(lname, pif) != NULL) {
1133*3431Scarlsonj 		dhcpmsg(MSG_ERROR, "attach_lif: entry for %s already exists!",
1134*3431Scarlsonj 		    lname);
1135*3431Scarlsonj 		release_pif(pif);
1136*3431Scarlsonj 		*error = DHCP_IPC_E_INVIF;
1137*3431Scarlsonj 		return (NULL);
1138*3431Scarlsonj 	}
1139*3431Scarlsonj 
1140*3431Scarlsonj 	/* If LIF creation fails, then insert_lif discards our PIF hold */
1141*3431Scarlsonj 	return (insert_lif(pif, lname, error));
1142*3431Scarlsonj }
1143*3431Scarlsonj 
1144*3431Scarlsonj /*
1145*3431Scarlsonj  * set_lif_dhcp(): Set logical interface flags to show that it's managed
1146*3431Scarlsonj  *		   by DHCP.
1147*3431Scarlsonj  *
1148*3431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
1149*3431Scarlsonj  *	    boolean_t: B_TRUE if adopting
1150*3431Scarlsonj  *  output: int: set to DHCP_IPC_E_* if operation fails
1151*3431Scarlsonj  */
1152*3431Scarlsonj 
1153*3431Scarlsonj int
1154*3431Scarlsonj set_lif_dhcp(dhcp_lif_t *lif, boolean_t is_adopting)
1155*3431Scarlsonj {
1156*3431Scarlsonj 	int fd;
1157*3431Scarlsonj 	int err;
1158*3431Scarlsonj 	struct lifreq lifr;
1159*3431Scarlsonj 
1160*3431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1161*3431Scarlsonj 
1162*3431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1163*3431Scarlsonj 
1164*3431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1165*3431Scarlsonj 		err = errno;
1166*3431Scarlsonj 		dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCGLIFFLAGS for %s",
1167*3431Scarlsonj 		    lif->lif_name);
1168*3431Scarlsonj 		return (err == ENXIO ? DHCP_IPC_E_INVIF : DHCP_IPC_E_INT);
1169*3431Scarlsonj 	}
1170*3431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
1171*3431Scarlsonj 
1172*3431Scarlsonj 	/*
1173*3431Scarlsonj 	 * Check for conflicting sources of address control, and other
1174*3431Scarlsonj 	 * unacceptable configurations.
1175*3431Scarlsonj 	 */
1176*3431Scarlsonj 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_MIPRUNNING|
1177*3431Scarlsonj 	    IFF_TEMPORARY|IFF_VIRTUAL)) {
1178*3431Scarlsonj 		dhcpmsg(MSG_ERR, "set_lif_dhcp: cannot use %s: flags are %llx",
1179*3431Scarlsonj 		    lif->lif_name, lifr.lifr_flags);
1180*3431Scarlsonj 		return (DHCP_IPC_E_INVIF);
1181*3431Scarlsonj 	}
1182*3431Scarlsonj 
11830Sstevel@tonic-gate 	/*
11840Sstevel@tonic-gate 	 * if DHCPRUNNING is already set on the interface and we're
11850Sstevel@tonic-gate 	 * not adopting it, the agent probably crashed and burned.
11860Sstevel@tonic-gate 	 * note it, but don't let it stop the proceedings.  we're
11870Sstevel@tonic-gate 	 * pretty sure we're not already running, since we wouldn't
11880Sstevel@tonic-gate 	 * have been able to bind to our IPC port.
11890Sstevel@tonic-gate 	 */
11900Sstevel@tonic-gate 
1191*3431Scarlsonj 	if (lifr.lifr_flags & IFF_DHCPRUNNING) {
1192*3431Scarlsonj 		if (!is_adopting) {
1193*3431Scarlsonj 			dhcpmsg(MSG_WARNING, "set_lif_dhcp: DHCP flag already "
1194*3431Scarlsonj 			    "set on %s", lif->lif_name);
1195*3431Scarlsonj 		}
1196*3431Scarlsonj 	} else {
1197*3431Scarlsonj 		lifr.lifr_flags |= IFF_DHCPRUNNING;
1198*3431Scarlsonj 		if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
1199*3431Scarlsonj 			dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s",
1200*3431Scarlsonj 			    lif->lif_name);
1201*3431Scarlsonj 			return (DHCP_IPC_E_INT);
1202*3431Scarlsonj 		}
1203*3431Scarlsonj 		lif->lif_flags = lifr.lifr_flags;
12040Sstevel@tonic-gate 	}
1205*3431Scarlsonj 	return (DHCP_IPC_SUCCESS);
1206*3431Scarlsonj }
12070Sstevel@tonic-gate 
1208*3431Scarlsonj /*
1209*3431Scarlsonj  * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer
1210*3431Scarlsonj  *		     managed by DHCP.
1211*3431Scarlsonj  *
1212*3431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
1213*3431Scarlsonj  *  output: none
1214*3431Scarlsonj  */
12150Sstevel@tonic-gate 
1216*3431Scarlsonj static void
1217*3431Scarlsonj clear_lif_dhcp(dhcp_lif_t *lif)
1218*3431Scarlsonj {
1219*3431Scarlsonj 	int fd;
1220*3431Scarlsonj 	struct lifreq lifr;
12210Sstevel@tonic-gate 
1222*3431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1223*3431Scarlsonj 
1224*3431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1225*3431Scarlsonj 
1226*3431Scarlsonj 	if (!(lif->lif_flags & IFF_DHCPRUNNING))
1227*3431Scarlsonj 		return;
12280Sstevel@tonic-gate 
1229*3431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
1230*3431Scarlsonj 		return;
12310Sstevel@tonic-gate 
1232*3431Scarlsonj 	if (!(lifr.lifr_flags & IFF_DHCPRUNNING))
1233*3431Scarlsonj 		return;
12340Sstevel@tonic-gate 
1235*3431Scarlsonj 	lif->lif_flags = lifr.lifr_flags &= ~IFF_DHCPRUNNING;
1236*3431Scarlsonj 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
1237*3431Scarlsonj }
12380Sstevel@tonic-gate 
1239*3431Scarlsonj /*
1240*3431Scarlsonj  * set_lif_deprecated(): Set the "deprecated" flag to tell users that this
1241*3431Scarlsonj  *			 address will be going away.  As the interface is
1242*3431Scarlsonj  *			 going away, we don't care if there are errors.
1243*3431Scarlsonj  *
1244*3431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
1245*3431Scarlsonj  *  output: none
1246*3431Scarlsonj  */
12470Sstevel@tonic-gate 
1248*3431Scarlsonj void
1249*3431Scarlsonj set_lif_deprecated(dhcp_lif_t *lif)
1250*3431Scarlsonj {
1251*3431Scarlsonj 	int fd;
1252*3431Scarlsonj 	struct lifreq lifr;
12530Sstevel@tonic-gate 
1254*3431Scarlsonj 	if (lif->lif_flags & IFF_DEPRECATED)
1255*3431Scarlsonj 		return;
12560Sstevel@tonic-gate 
1257*3431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1258*3431Scarlsonj 
1259*3431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
12600Sstevel@tonic-gate 
1261*3431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1)
1262*3431Scarlsonj 		return;
1263*3431Scarlsonj 
1264*3431Scarlsonj 	if (lifr.lifr_flags & IFF_DEPRECATED)
1265*3431Scarlsonj 		return;
12660Sstevel@tonic-gate 
1267*3431Scarlsonj 	lifr.lifr_flags |= IFF_DEPRECATED;
1268*3431Scarlsonj 	(void) ioctl(fd, SIOCSLIFFLAGS, &lifr);
1269*3431Scarlsonj 	lif->lif_flags = lifr.lifr_flags;
1270*3431Scarlsonj }
1271*3431Scarlsonj 
1272*3431Scarlsonj /*
1273*3431Scarlsonj  * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this
1274*3431Scarlsonj  *			   address will not be going away.  This happens if we
1275*3431Scarlsonj  *			   get a renewal after preferred lifetime but before
1276*3431Scarlsonj  *			   the valid lifetime.
1277*3431Scarlsonj  *
1278*3431Scarlsonj  *   input: dhcp_lif_t *: the logical interface
1279*3431Scarlsonj  *  output: boolean_t: B_TRUE on success.
1280*3431Scarlsonj  */
12810Sstevel@tonic-gate 
1282*3431Scarlsonj boolean_t
1283*3431Scarlsonj clear_lif_deprecated(dhcp_lif_t *lif)
1284*3431Scarlsonj {
1285*3431Scarlsonj 	int fd;
1286*3431Scarlsonj 	struct lifreq lifr;
1287*3431Scarlsonj 
1288*3431Scarlsonj 	fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
1289*3431Scarlsonj 
1290*3431Scarlsonj 	(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
1291*3431Scarlsonj 
1292*3431Scarlsonj 	if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
1293*3431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCGLIFFLAGS for %s",
1294*3431Scarlsonj 		    lif->lif_name);
1295*3431Scarlsonj 		return (B_FALSE);
12960Sstevel@tonic-gate 	}
12970Sstevel@tonic-gate 
12980Sstevel@tonic-gate 	/*
1299*3431Scarlsonj 	 * Check for conflicting sources of address control, and other
1300*3431Scarlsonj 	 * unacceptable configurations.
13010Sstevel@tonic-gate 	 */
1302*3431Scarlsonj 	if (lifr.lifr_flags & (IFF_LOOPBACK|IFF_ADDRCONF|IFF_MIPRUNNING|
1303*3431Scarlsonj 	    IFF_TEMPORARY|IFF_VIRTUAL)) {
1304*3431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: cannot use %s: flags "
1305*3431Scarlsonj 		    "are %llx", lif->lif_name, lifr.lifr_flags);
1306*3431Scarlsonj 		return (B_FALSE);
13070Sstevel@tonic-gate 	}
13080Sstevel@tonic-gate 
1309*3431Scarlsonj 	if (!(lifr.lifr_flags & IFF_DEPRECATED))
1310*3431Scarlsonj 		return (B_TRUE);
13110Sstevel@tonic-gate 
1312*3431Scarlsonj 	lifr.lifr_flags &= ~IFF_DEPRECATED;
1313*3431Scarlsonj 	if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
1314*3431Scarlsonj 		dhcpmsg(MSG_ERR, "clear_lif_deprecated: SIOCSLIFFLAGS for %s",
1315*3431Scarlsonj 		    lif->lif_name);
1316*3431Scarlsonj 		return (B_FALSE);
1317*3431Scarlsonj 	} else {
1318*3431Scarlsonj 		lif->lif_flags = lifr.lifr_flags;
1319*3431Scarlsonj 		return (B_TRUE);
13200Sstevel@tonic-gate 	}
13210Sstevel@tonic-gate }
13220Sstevel@tonic-gate 
13230Sstevel@tonic-gate /*
1324*3431Scarlsonj  * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only).
13250Sstevel@tonic-gate  *
1326*3431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
1327*3431Scarlsonj  *  output: boolean_t: B_TRUE if the socket was opened successfully.
13280Sstevel@tonic-gate  */
13290Sstevel@tonic-gate 
1330*3431Scarlsonj boolean_t
1331*3431Scarlsonj open_ip_lif(dhcp_lif_t *lif)
13320Sstevel@tonic-gate {
1333*3431Scarlsonj 	if (lif->lif_sock_ip_fd != -1) {
1334*3431Scarlsonj 		dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s",
1335*3431Scarlsonj 		    lif->lif_name);
1336*3431Scarlsonj 		return (B_FALSE);
13370Sstevel@tonic-gate 	}
13380Sstevel@tonic-gate 
1339*3431Scarlsonj 	lif->lif_sock_ip_fd = socket(AF_INET, SOCK_DGRAM, 0);
1340*3431Scarlsonj 	if (lif->lif_sock_ip_fd == -1) {
1341*3431Scarlsonj 		dhcpmsg(MSG_ERR, "open_ip_lif: cannot create v4 socket on %s",
1342*3431Scarlsonj 		    lif->lif_name);
1343*3431Scarlsonj 		return (B_FALSE);
13440Sstevel@tonic-gate 	}
13450Sstevel@tonic-gate 
1346*3431Scarlsonj 	if (!bind_sock(lif->lif_sock_ip_fd, IPPORT_BOOTPC,
1347*3431Scarlsonj 	    ntohl(lif->lif_addr))) {
1348*3431Scarlsonj 		dhcpmsg(MSG_ERR, "open_ip_lif: cannot bind v4 socket on %s",
1349*3431Scarlsonj 		    lif->lif_name);
1350*3431Scarlsonj 		return (B_FALSE);
1351*3431Scarlsonj 	}
13520Sstevel@tonic-gate 
1353*3431Scarlsonj 	lif->lif_acknak_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN,
1354*3431Scarlsonj 	    dhcp_acknak_lif, lif);
1355*3431Scarlsonj 	if (lif->lif_acknak_id == -1) {
1356*3431Scarlsonj 		dhcpmsg(MSG_WARNING, "open_ip_lif: cannot register to "
1357*3431Scarlsonj 		    "receive IP unicast");
1358*3431Scarlsonj 		close_ip_lif(lif);
1359*3431Scarlsonj 		return (B_FALSE);
13600Sstevel@tonic-gate 	}
13610Sstevel@tonic-gate 	return (B_TRUE);
13620Sstevel@tonic-gate }
13630Sstevel@tonic-gate 
13640Sstevel@tonic-gate /*
1365*3431Scarlsonj  * close_ip_lif(): close an IP socket for I/O on a given LIF.
13660Sstevel@tonic-gate  *
1367*3431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
1368*3431Scarlsonj  *  output: none
13690Sstevel@tonic-gate  */
13700Sstevel@tonic-gate 
1371*3431Scarlsonj void
1372*3431Scarlsonj close_ip_lif(dhcp_lif_t *lif)
13730Sstevel@tonic-gate {
1374*3431Scarlsonj 	if (lif->lif_acknak_id != -1) {
1375*3431Scarlsonj 		(void) iu_unregister_event(eh, lif->lif_acknak_id, NULL);
1376*3431Scarlsonj 		lif->lif_acknak_id = -1;
1377*3431Scarlsonj 	}
1378*3431Scarlsonj 	if (lif->lif_sock_ip_fd != -1) {
1379*3431Scarlsonj 		(void) close(lif->lif_sock_ip_fd);
1380*3431Scarlsonj 		lif->lif_sock_ip_fd = -1;
1381*3431Scarlsonj 	}
1382*3431Scarlsonj }
13830Sstevel@tonic-gate 
1384*3431Scarlsonj /*
1385*3431Scarlsonj  * lif_mark_decline(): mark a LIF as having been declined due to a duplicate
1386*3431Scarlsonj  *		       address or some other conflict.  This is used in
1387*3431Scarlsonj  *		       send_declines() to report failure back to the server.
1388*3431Scarlsonj  *
1389*3431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
1390*3431Scarlsonj  *	    const char *: text string explaining why the address is declined
1391*3431Scarlsonj  *  output: none
1392*3431Scarlsonj  */
13930Sstevel@tonic-gate 
1394*3431Scarlsonj void
1395*3431Scarlsonj lif_mark_decline(dhcp_lif_t *lif, const char *reason)
1396*3431Scarlsonj {
1397*3431Scarlsonj 	if (lif->lif_declined == NULL) {
1398*3431Scarlsonj 		dhcp_lease_t *dlp;
13990Sstevel@tonic-gate 
1400*3431Scarlsonj 		lif->lif_declined = reason;
1401*3431Scarlsonj 		if ((dlp = lif->lif_lease) != NULL)
1402*3431Scarlsonj 			dlp->dl_smach->dsm_lif_down++;
14030Sstevel@tonic-gate 	}
14040Sstevel@tonic-gate }
14050Sstevel@tonic-gate 
14060Sstevel@tonic-gate /*
1407*3431Scarlsonj  * schedule_lif_timer(): schedules the LIF-related timer
14080Sstevel@tonic-gate  *
1409*3431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
1410*3431Scarlsonj  *	    dhcp_timer_t *: the timer to schedule
1411*3431Scarlsonj  *	    iu_tq_callback_t *: the callback to call upon firing
1412*3431Scarlsonj  *  output: boolean_t: B_TRUE if the timer was scheduled successfully
14130Sstevel@tonic-gate  */
14140Sstevel@tonic-gate 
1415*3431Scarlsonj boolean_t
1416*3431Scarlsonj schedule_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt, iu_tq_callback_t *expire)
14170Sstevel@tonic-gate {
14180Sstevel@tonic-gate 	/*
1419*3431Scarlsonj 	 * If there's a timer running, cancel it and release its lease
1420*3431Scarlsonj 	 * reference.
14210Sstevel@tonic-gate 	 */
1422*3431Scarlsonj 	if (dt->dt_id != -1) {
1423*3431Scarlsonj 		if (!cancel_timer(dt))
1424*3431Scarlsonj 			return (B_FALSE);
1425*3431Scarlsonj 		release_lif(lif);
1426*3431Scarlsonj 	}
14270Sstevel@tonic-gate 
1428*3431Scarlsonj 	if (schedule_timer(dt, expire, lif)) {
1429*3431Scarlsonj 		hold_lif(lif);
1430*3431Scarlsonj 		return (B_TRUE);
1431*3431Scarlsonj 	} else {
1432*3431Scarlsonj 		dhcpmsg(MSG_WARNING,
1433*3431Scarlsonj 		    "schedule_lif_timer: cannot schedule timer");
1434*3431Scarlsonj 		return (B_FALSE);
1435*3431Scarlsonj 	}
14360Sstevel@tonic-gate }
14370Sstevel@tonic-gate 
14380Sstevel@tonic-gate /*
1439*3431Scarlsonj  * cancel_lif_timer(): cancels a LIF-related timer
14400Sstevel@tonic-gate  *
1441*3431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
1442*3431Scarlsonj  *	    dhcp_timer_t *: the timer to cancel
1443*3431Scarlsonj  *  output: none
14440Sstevel@tonic-gate  */
14450Sstevel@tonic-gate 
1446*3431Scarlsonj static void
1447*3431Scarlsonj cancel_lif_timer(dhcp_lif_t *lif, dhcp_timer_t *dt)
14480Sstevel@tonic-gate {
1449*3431Scarlsonj 	if (dt->dt_id == -1)
1450*3431Scarlsonj 		return;
1451*3431Scarlsonj 	if (cancel_timer(dt)) {
1452*3431Scarlsonj 		dhcpmsg(MSG_DEBUG2,
1453*3431Scarlsonj 		    "cancel_lif_timer: canceled expiry timer on %s",
1454*3431Scarlsonj 		    lif->lif_name);
1455*3431Scarlsonj 		release_lif(lif);
1456*3431Scarlsonj 	} else {
1457*3431Scarlsonj 		dhcpmsg(MSG_WARNING,
1458*3431Scarlsonj 		    "cancel_lif_timer: cannot cancel timer on %s",
1459*3431Scarlsonj 		    lif->lif_name);
14600Sstevel@tonic-gate 	}
14610Sstevel@tonic-gate }
14620Sstevel@tonic-gate 
14630Sstevel@tonic-gate /*
1464*3431Scarlsonj  * cancel_lif_timers(): cancels the LIF-related timers
14650Sstevel@tonic-gate  *
1466*3431Scarlsonj  *   input: dhcp_lif_t *: the logical interface to operate on
1467*3431Scarlsonj  *  output: none
14680Sstevel@tonic-gate  */
14690Sstevel@tonic-gate 
14700Sstevel@tonic-gate void
1471*3431Scarlsonj cancel_lif_timers(dhcp_lif_t *lif)
14720Sstevel@tonic-gate {
1473*3431Scarlsonj 	cancel_lif_timer(lif, &lif->lif_preferred);
1474*3431Scarlsonj 	cancel_lif_timer(lif, &lif->lif_expire);
14750Sstevel@tonic-gate }
14760Sstevel@tonic-gate 
14770Sstevel@tonic-gate /*
1478*3431Scarlsonj  * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common
1479*3431Scarlsonj  *		  file descriptors (v4_sock_fd and v6_sock_fd).
14800Sstevel@tonic-gate  *
1481*3431Scarlsonj  *   input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4
1482*3431Scarlsonj  *  output: none
14830Sstevel@tonic-gate  */
14840Sstevel@tonic-gate 
1485*3431Scarlsonj uint_t
1486*3431Scarlsonj get_max_mtu(boolean_t isv6)
14870Sstevel@tonic-gate {
1488*3431Scarlsonj 	uint_t *mtup = isv6 ? &cached_v6_max_mtu : &cached_v4_max_mtu;
1489*3431Scarlsonj 
1490*3431Scarlsonj 	if (*mtup == 0) {
1491*3431Scarlsonj 		dhcp_pif_t *pif;
1492*3431Scarlsonj 		dhcp_lif_t *lif;
1493*3431Scarlsonj 		struct lifreq lifr;
1494*3431Scarlsonj 
1495*3431Scarlsonj 		/* Set an arbitrary lower bound */
1496*3431Scarlsonj 		*mtup = 1024;
1497*3431Scarlsonj 		pif = isv6 ? v6root : v4root;
1498*3431Scarlsonj 		for (; pif != NULL; pif = pif->pif_next) {
1499*3431Scarlsonj 			for (lif = pif->pif_lifs; lif != NULL;
1500*3431Scarlsonj 			    lif = lif->lif_next) {
1501*3431Scarlsonj 				(void) strlcpy(lifr.lifr_name, lif->lif_name,
1502*3431Scarlsonj 				    LIFNAMSIZ);
1503*3431Scarlsonj 				if (ioctl(v4_sock_fd, SIOCGLIFMTU, &lifr) !=
1504*3431Scarlsonj 				    -1 && lifr.lifr_mtu > *mtup) {
1505*3431Scarlsonj 					*mtup = lifr.lifr_mtu;
1506*3431Scarlsonj 				}
1507*3431Scarlsonj 			}
1508*3431Scarlsonj 		}
15090Sstevel@tonic-gate 	}
1510*3431Scarlsonj 	return (*mtup);
15110Sstevel@tonic-gate }
15120Sstevel@tonic-gate 
15130Sstevel@tonic-gate /*
1514*3431Scarlsonj  * expired_lif_state(): summarize the state of expired LIFs on a given state
1515*3431Scarlsonj  *			machine.
15160Sstevel@tonic-gate  *
1517*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine to scan
1518*3431Scarlsonj  *  output: dhcp_expire_t: overall state
15190Sstevel@tonic-gate  */
15200Sstevel@tonic-gate 
1521*3431Scarlsonj dhcp_expire_t
1522*3431Scarlsonj expired_lif_state(dhcp_smach_t *dsmp)
15230Sstevel@tonic-gate {
1524*3431Scarlsonj 	dhcp_lease_t *dlp;
1525*3431Scarlsonj 	dhcp_lif_t *lif;
1526*3431Scarlsonj 	uint_t nlifs;
1527*3431Scarlsonj 	uint_t numlifs;
1528*3431Scarlsonj 	uint_t numexp;
15290Sstevel@tonic-gate 
1530*3431Scarlsonj 	numlifs = numexp = 0;
1531*3431Scarlsonj 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
1532*3431Scarlsonj 		lif = dlp->dl_lifs;
1533*3431Scarlsonj 		nlifs = dlp->dl_nlifs;
1534*3431Scarlsonj 		numlifs += nlifs;
1535*3431Scarlsonj 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
1536*3431Scarlsonj 			if (lif->lif_expired)
1537*3431Scarlsonj 				numexp++;
1538*3431Scarlsonj 		}
15390Sstevel@tonic-gate 	}
1540*3431Scarlsonj 	if (numlifs == 0)
1541*3431Scarlsonj 		return (DHCP_EXP_NOLIFS);
1542*3431Scarlsonj 	else if (numexp == 0)
1543*3431Scarlsonj 		return (DHCP_EXP_NOEXP);
1544*3431Scarlsonj 	else if (numlifs == numexp)
1545*3431Scarlsonj 		return (DHCP_EXP_ALLEXP);
1546*3431Scarlsonj 	else
1547*3431Scarlsonj 		return (DHCP_EXP_SOMEEXP);
15480Sstevel@tonic-gate }
15490Sstevel@tonic-gate 
15500Sstevel@tonic-gate /*
1551*3431Scarlsonj  * find_expired_lif(): find the first expired LIF on a given state machine
15520Sstevel@tonic-gate  *
1553*3431Scarlsonj  *   input: dhcp_smach_t *: the state machine to scan
1554*3431Scarlsonj  *  output: dhcp_lif_t *: the first expired LIF, or NULL if none.
15550Sstevel@tonic-gate  */
15560Sstevel@tonic-gate 
1557*3431Scarlsonj dhcp_lif_t *
1558*3431Scarlsonj find_expired_lif(dhcp_smach_t *dsmp)
15590Sstevel@tonic-gate {
1560*3431Scarlsonj 	dhcp_lease_t *dlp;
1561*3431Scarlsonj 	dhcp_lif_t *lif;
1562*3431Scarlsonj 	uint_t nlifs;
1563*3431Scarlsonj 
1564*3431Scarlsonj 	for (dlp = dsmp->dsm_leases; dlp != NULL; dlp = dlp->dl_next) {
1565*3431Scarlsonj 		lif = dlp->dl_lifs;
1566*3431Scarlsonj 		nlifs = dlp->dl_nlifs;
1567*3431Scarlsonj 		for (; nlifs > 0; nlifs--, lif = lif->lif_next) {
1568*3431Scarlsonj 			if (lif->lif_expired)
1569*3431Scarlsonj 				return (lif);
1570*3431Scarlsonj 		}
1571*3431Scarlsonj 	}
1572*3431Scarlsonj 	return (NULL);
1573*3431Scarlsonj }
1574*3431Scarlsonj 
1575*3431Scarlsonj /*
1576*3431Scarlsonj  * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING.  Used
1577*3431Scarlsonj  *		       only for DHCPv6.
1578*3431Scarlsonj  *
1579*3431Scarlsonj  *   input: none
1580*3431Scarlsonj  *  output: none
1581*3431Scarlsonj  */
1582*3431Scarlsonj 
1583*3431Scarlsonj void
1584*3431Scarlsonj remove_v6_strays(void)
1585*3431Scarlsonj {
1586*3431Scarlsonj 	struct lifnum lifn;
1587*3431Scarlsonj 	struct lifconf lifc;
1588*3431Scarlsonj 	struct lifreq *lifrp, *lifrmax;
1589*3431Scarlsonj 	uint_t numifs;
1590*3431Scarlsonj 	uint64_t flags;
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate 	/*
1593*3431Scarlsonj 	 * Get the approximate number of interfaces in the system.  It's only
1594*3431Scarlsonj 	 * approximate because the system is dynamic -- interfaces may be
1595*3431Scarlsonj 	 * plumbed or unplumbed at any time.  This is also the reason for the
1596*3431Scarlsonj 	 * "+ 10" fudge factor: we're trying to avoid unnecessary looping.
15970Sstevel@tonic-gate 	 */
1598*3431Scarlsonj 	(void) memset(&lifn, 0, sizeof (lifn));
1599*3431Scarlsonj 	lifn.lifn_family = AF_INET6;
1600*3431Scarlsonj 	lifn.lifn_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
1601*3431Scarlsonj 	if (ioctl(v6_sock_fd, SIOCGLIFNUM, &lifn) == -1) {
1602*3431Scarlsonj 		dhcpmsg(MSG_ERR,
1603*3431Scarlsonj 		    "remove_v6_strays: cannot read number of interfaces");
1604*3431Scarlsonj 		numifs = 10;
1605*3431Scarlsonj 	} else {
1606*3431Scarlsonj 		numifs = lifn.lifn_count + 10;
16070Sstevel@tonic-gate 	}
16080Sstevel@tonic-gate 
16090Sstevel@tonic-gate 	/*
1610*3431Scarlsonj 	 * Get the interface information.  We do this in a loop so that we can
1611*3431Scarlsonj 	 * recover from EINVAL from the kernel -- delivered when the buffer is
1612*3431Scarlsonj 	 * too small.
16130Sstevel@tonic-gate 	 */
1614*3431Scarlsonj 	(void) memset(&lifc, 0, sizeof (lifc));
1615*3431Scarlsonj 	lifc.lifc_family = AF_INET6;
1616*3431Scarlsonj 	lifc.lifc_flags = LIFC_ALLZONES | LIFC_NOXMIT | LIFC_TEMPORARY;
1617*3431Scarlsonj 	for (;;) {
1618*3431Scarlsonj 		lifc.lifc_len = numifs * sizeof (*lifrp);
1619*3431Scarlsonj 		lifrp = realloc(lifc.lifc_buf, lifc.lifc_len);
1620*3431Scarlsonj 		if (lifrp == NULL) {
1621*3431Scarlsonj 			dhcpmsg(MSG_ERR,
1622*3431Scarlsonj 			    "remove_v6_strays: cannot allocate memory");
1623*3431Scarlsonj 			free(lifc.lifc_buf);
1624*3431Scarlsonj 			return;
16250Sstevel@tonic-gate 		}
1626*3431Scarlsonj 		lifc.lifc_buf = (caddr_t)lifrp;
1627*3431Scarlsonj 		errno = 0;
1628*3431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFCONF, &lifc) == 0 &&
1629*3431Scarlsonj 		    lifc.lifc_len < numifs * sizeof (*lifrp))
16300Sstevel@tonic-gate 			break;
1631*3431Scarlsonj 		if (errno == 0 || errno == EINVAL) {
1632*3431Scarlsonj 			numifs <<= 1;
1633*3431Scarlsonj 		} else {
1634*3431Scarlsonj 			dhcpmsg(MSG_ERR, "remove_v6_strays: SIOCGLIFCONF");
1635*3431Scarlsonj 			free(lifc.lifc_buf);
1636*3431Scarlsonj 			return;
16370Sstevel@tonic-gate 		}
16380Sstevel@tonic-gate 	}
16390Sstevel@tonic-gate 
1640*3431Scarlsonj 	lifrmax = lifrp + lifc.lifc_len / sizeof (*lifrp);
1641*3431Scarlsonj 	for (; lifrp < lifrmax; lifrp++) {
16420Sstevel@tonic-gate 		/*
1643*3431Scarlsonj 		 * Get the interface flags; we're interested in the DHCP ones.
16440Sstevel@tonic-gate 		 */
1645*3431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFFLAGS, lifrp) == -1)
1646*3431Scarlsonj 			continue;
1647*3431Scarlsonj 		flags = lifrp->lifr_flags;
1648*3431Scarlsonj 		if (!(flags & IFF_DHCPRUNNING))
1649*3431Scarlsonj 			continue;
16500Sstevel@tonic-gate 		/*
1651*3431Scarlsonj 		 * If the interface has a link-local address, then we don't
1652*3431Scarlsonj 		 * control it.  Just remove the flag.
16530Sstevel@tonic-gate 		 */
1654*3431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCGLIFADDR, lifrp) == -1)
1655*3431Scarlsonj 			continue;
1656*3431Scarlsonj 		if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)&lifrp->
1657*3431Scarlsonj 		    lifr_addr)->sin6_addr)) {
1658*3431Scarlsonj 			lifrp->lifr_flags = flags & ~IFF_DHCPRUNNING;
1659*3431Scarlsonj 			(void) ioctl(v6_sock_fd, SIOCSLIFFLAGS, lifrp);
1660*3431Scarlsonj 			continue;
16610Sstevel@tonic-gate 		}
16620Sstevel@tonic-gate 		/*
1663*3431Scarlsonj 		 * All others are (or were) under our control.  Clean up by
1664*3431Scarlsonj 		 * removing them.
16650Sstevel@tonic-gate 		 */
1666*3431Scarlsonj 		if (ioctl(v6_sock_fd, SIOCLIFREMOVEIF, lifrp) == 0) {
1667*3431Scarlsonj 			dhcpmsg(MSG_DEBUG, "remove_v6_strays: removed %s",
1668*3431Scarlsonj 			    lifrp->lifr_name);
1669*3431Scarlsonj 		} else if (errno != ENXIO) {
1670*3431Scarlsonj 			dhcpmsg(MSG_ERR,
1671*3431Scarlsonj 			    "remove_v6_strays: SIOCLIFREMOVEIF %s",
1672*3431Scarlsonj 			    lifrp->lifr_name);
16730Sstevel@tonic-gate 		}
16740Sstevel@tonic-gate 	}
1675*3431Scarlsonj 	free(lifc.lifc_buf);
16760Sstevel@tonic-gate }
1677