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