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