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