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