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