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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/types.h>
27 #include <sys/socket.h>
30 #include <sys/sockio.h>
31 #include <netinet/in.h>
32 #include <netinet/dhcp.h>
36 #include <libdevinfo.h>
38 #include <netinet/if_ether.h>
39 #include <arpa/inet.h>
43 #include "interface.h"
51 static uint_t cached_v4_max_mtu
, cached_v6_max_mtu
;
54 * Interface flags to watch: things that should be under our direct control.
56 #define DHCP_IFF_WATCH (IFF_DHCPRUNNING | IFF_DEPRECATED | IFF_ADDRCONF | \
59 static void clear_lif_dhcp(dhcp_lif_t
*);
62 * insert_pif(): creates a new physical interface structure and chains it on
63 * the list. Initializes state that remains consistent across
64 * all use of the physical interface entry.
66 * input: const char *: the name of the physical interface
67 * boolean_t: if B_TRUE, this is DHCPv6
68 * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
69 * error code with the reason why
70 * output: dhcp_pif_t *: a pointer to the new entry, or NULL on failure
74 insert_pif(const char *pname
, boolean_t isv6
, int *error
)
79 dlpi_handle_t dh
= NULL
;
80 int fd
= isv6
? v6_sock_fd
: v4_sock_fd
;
82 if ((pif
= calloc(1, sizeof (*pif
))) == NULL
) {
83 dhcpmsg(MSG_ERR
, "insert_pif: cannot allocate pif entry for "
85 *error
= DHCP_IPC_E_MEMORY
;
90 pif
->pif_hold_count
= 1;
91 pif
->pif_running
= B_TRUE
;
93 if (strlcpy(pif
->pif_name
, pname
, LIFNAMSIZ
) >= LIFNAMSIZ
) {
94 dhcpmsg(MSG_ERROR
, "insert_pif: interface name %s is too long",
96 *error
= DHCP_IPC_E_INVIF
;
101 * This is a bit gross, but IP has a confused interface. We must
102 * assume that the zeroth LIF is plumbed, and must query there to get
103 * the interface index number.
105 (void) strlcpy(lifr
.lifr_name
, pname
, LIFNAMSIZ
);
107 if (ioctl(fd
, SIOCGLIFINDEX
, &lifr
) == -1) {
108 *error
= (errno
== ENXIO
) ? DHCP_IPC_E_INVIF
: DHCP_IPC_E_INT
;
109 dhcpmsg(MSG_ERR
, "insert_pif: SIOCGLIFINDEX for %s", pname
);
112 pif
->pif_index
= lifr
.lifr_index
;
115 * Check if this is a VRRP interface. If yes, its IP addresses (the
116 * VRRP virtual addresses) cannot be configured using DHCP.
118 if (ioctl(fd
, SIOCGLIFFLAGS
, &lifr
) == -1) {
119 *error
= (errno
== ENXIO
) ? DHCP_IPC_E_INVIF
: DHCP_IPC_E_INT
;
120 dhcpmsg(MSG_ERR
, "insert_pif: SIOCGLIFFLAGS for %s", pname
);
124 if (lifr
.lifr_flags
& IFF_VRRP
) {
125 *error
= DHCP_IPC_E_INVIF
;
126 dhcpmsg(MSG_ERROR
, "insert_pif: VRRP virtual addresses over %s "
127 "cannot be configured using DHCP", pname
);
131 if (ioctl(fd
, SIOCGLIFMTU
, &lifr
) == -1) {
132 *error
= (errno
== ENXIO
) ? DHCP_IPC_E_INVIF
: DHCP_IPC_E_INT
;
133 dhcpmsg(MSG_ERR
, "insert_pif: SIOCGLIFMTU for %s", pname
);
136 pif
->pif_max
= lifr
.lifr_mtu
;
138 if (pif
->pif_max
< DHCP_DEF_MAX_SIZE
) {
139 dhcpmsg(MSG_ERROR
, "insert_pif: MTU of %s is too small to "
140 "support DHCP (%u < %u)", pname
, pif
->pif_max
,
142 *error
= DHCP_IPC_E_INVIF
;
147 * Check if the pif is in an IPMP group. Interfaces using IPMP don't
148 * have dedicated hardware addresses, and get their hardware type from
149 * the SIOCGLIFGROUPINFO ioctl rather than DLPI.
151 if (ioctl(fd
, SIOCGLIFGROUPNAME
, &lifr
) == -1) {
152 *error
= DHCP_IPC_E_INT
;
153 dhcpmsg(MSG_ERR
, "insert_pif: SIOCGLIFGROUPNAME for %s", pname
);
157 if (lifr
.lifr_groupname
[0] != '\0') {
158 (void) strlcpy(lifgr
.gi_grname
, lifr
.lifr_groupname
,
160 if (ioctl(fd
, SIOCGLIFGROUPINFO
, &lifgr
) == -1) {
161 *error
= DHCP_IPC_E_INT
;
162 dhcpmsg(MSG_ERR
, "insert_pif: SIOCGLIFGROUPINFO for %s",
167 pif
->pif_hwtype
= dlpi_arptype(lifgr
.gi_mactype
);
168 pif
->pif_under_ipmp
= (strcmp(pname
, lifgr
.gi_grifname
) != 0);
169 (void) strlcpy(pif
->pif_grifname
, lifgr
.gi_grifname
, LIFNAMSIZ
);
172 * For IPMP underlying interfaces, stash the interface index
173 * of the IPMP meta-interface; we'll use it to send/receive
174 * traffic. This is both necessary (since IP_BOUND_IF for
175 * non-unicast traffic won't work on underlying interfaces)
176 * and preferred (since a test address lease will be able to
177 * be maintained as long as another interface in the group is
178 * still functioning).
180 if (pif
->pif_under_ipmp
) {
181 (void) strlcpy(lifr
.lifr_name
, pif
->pif_grifname
,
184 if (ioctl(fd
, SIOCGLIFINDEX
, &lifr
) == -1) {
185 *error
= DHCP_IPC_E_INT
;
186 dhcpmsg(MSG_ERR
, "insert_pif: SIOCGLIFINDEX "
187 "for %s", lifr
.lifr_name
);
190 pif
->pif_grindex
= lifr
.lifr_index
;
195 * For IPv4, if the hardware type is still unknown, use DLPI to
196 * determine it, the hardware address, and hardware address length.
198 if (!isv6
&& pif
->pif_hwtype
== 0) {
202 if ((rc
= dlpi_open(pname
, &dh
, 0)) != DLPI_SUCCESS
) {
203 dhcpmsg(MSG_ERROR
, "insert_pif: dlpi_open: %s",
205 *error
= DHCP_IPC_E_INVIF
;
209 if ((rc
= dlpi_bind(dh
, ETHERTYPE_IP
, NULL
)) != DLPI_SUCCESS
) {
210 dhcpmsg(MSG_ERROR
, "insert_pif: dlpi_bind: %s",
212 *error
= DHCP_IPC_E_INVIF
;
216 if ((rc
= dlpi_info(dh
, &dlinfo
, 0)) != DLPI_SUCCESS
) {
217 dhcpmsg(MSG_ERROR
, "insert_pif: dlpi_info: %s",
219 *error
= DHCP_IPC_E_INVIF
;
223 pif
->pif_hwtype
= dlpi_arptype(dlinfo
.di_mactype
);
224 pif
->pif_hwlen
= dlinfo
.di_physaddrlen
;
226 dhcpmsg(MSG_DEBUG
, "insert_pif: %s: hwtype %d, hwlen %d",
227 pname
, pif
->pif_hwtype
, pif
->pif_hwlen
);
229 if (pif
->pif_hwlen
> 0) {
230 pif
->pif_hwaddr
= malloc(pif
->pif_hwlen
);
231 if (pif
->pif_hwaddr
== NULL
) {
232 dhcpmsg(MSG_ERR
, "insert_pif: cannot allocate "
233 "pif_hwaddr for %s", pname
);
234 *error
= DHCP_IPC_E_MEMORY
;
237 (void) memcpy(pif
->pif_hwaddr
, dlinfo
.di_physaddr
,
245 insque(pif
, isv6
? &v6root
: &v4root
);
256 * hold_pif(): acquire a hold on a physical interface structure.
258 * input: dhcp_pif_t *: a pointer to the PIF structure
263 hold_pif(dhcp_pif_t
*pif
)
265 pif
->pif_hold_count
++;
266 dhcpmsg(MSG_DEBUG2
, "hold_pif: hold count on %s: %u", pif
->pif_name
,
267 pif
->pif_hold_count
);
271 * release_pif(): release a hold on a physical interface structure; will
272 * destroy the structure on the last hold removed.
274 * input: dhcp_pif_t *: a pointer to the PIF structure
279 release_pif(dhcp_pif_t
*pif
)
281 if (pif
->pif_hold_count
== 0) {
282 dhcpmsg(MSG_CRIT
, "release_pif: extraneous release");
286 if (--pif
->pif_hold_count
== 0) {
287 dhcpmsg(MSG_DEBUG
, "release_pif: freeing PIF %s",
291 free(pif
->pif_hwaddr
);
294 dhcpmsg(MSG_DEBUG2
, "release_pif: hold count on %s: %u",
295 pif
->pif_name
, pif
->pif_hold_count
);
300 * lookup_pif_by_uindex(): Looks up PIF entries given truncated index and
301 * previous PIF pointer (or NULL for list start).
302 * Caller is expected to iterate through all
303 * potential matches to find interface of interest.
305 * input: uint16_t: the interface index (truncated)
306 * dhcp_pif_t *: the previous PIF, or NULL for list start
307 * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
308 * output: dhcp_pif_t *: the next matching PIF, or NULL if not found
309 * note: This operates using the 'truncated' (16-bit) ifindex as seen by
310 * routing socket clients. The value stored in pif_index is the
311 * 32-bit ifindex from the ioctl interface.
315 lookup_pif_by_uindex(uint16_t ifindex
, dhcp_pif_t
*pif
, boolean_t isv6
)
318 pif
= isv6
? v6root
: v4root
;
322 for (; pif
!= NULL
; pif
= pif
->pif_next
) {
323 if ((pif
->pif_index
& 0xffff) == ifindex
)
331 * lookup_pif_by_name(): Looks up a physical interface entry given a name.
333 * input: const char *: the physical interface name
334 * boolean_t: B_TRUE if using DHCPv6, B_FALSE otherwise
335 * output: dhcp_pif_t *: the matching PIF, or NULL if not found
339 lookup_pif_by_name(const char *pname
, boolean_t isv6
)
343 pif
= isv6
? v6root
: v4root
;
345 for (; pif
!= NULL
; pif
= pif
->pif_next
) {
346 if (strcmp(pif
->pif_name
, pname
) == 0)
354 * pif_status(): update the physical interface up/down status.
356 * input: dhcp_pif_t *: the physical interface to be updated
357 * boolean_t: B_TRUE if the interface is going up
362 pif_status(dhcp_pif_t
*pif
, boolean_t isup
)
367 pif
->pif_running
= isup
;
368 dhcpmsg(MSG_DEBUG
, "interface %s has %s", pif
->pif_name
,
369 isup
? "come back up" : "gone down");
370 for (lif
= pif
->pif_lifs
; lif
!= NULL
; lif
= lif
->lif_next
) {
371 for (dsmp
= lif
->lif_smachs
; dsmp
!= NULL
;
372 dsmp
= dsmp
->dsm_next
) {
376 remove_default_routes(dsmp
);
381 /* Helper for insert_lif: extract addresses as defined */
382 #define ASSIGN_ADDR(v4, v6, lf) \
383 if (pif->pif_isv6) { \
384 lif->v6 = ((struct sockaddr_in6 *)&lifr.lf)->sin6_addr; \
386 lif->v4 = ((struct sockaddr_in *)&lifr.lf)->sin_addr.s_addr; \
390 * insert_lif(): Creates a new logical interface structure and chains it on
391 * the list for a given physical interface. Initializes state
392 * that remains consistent across all use of the logical
393 * interface entry. Caller's PIF hold is transferred to the
394 * LIF on success, and is dropped on failure.
396 * input: dhcp_pif_t *: pointer to the physical interface for this LIF
397 * const char *: the name of the logical interface
398 * int *: ignored on input; if insert_pif fails, set to a DHCP_IPC_E_*
399 * error code with the reason why
400 * output: dhcp_lif_t *: a pointer to the new entry, or NULL on failure
404 insert_lif(dhcp_pif_t
*pif
, const char *lname
, int *error
)
410 if ((lif
= calloc(1, sizeof (*lif
))) == NULL
) {
411 dhcpmsg(MSG_ERR
, "insert_lif: cannot allocate lif entry for "
413 *error
= DHCP_IPC_E_MEMORY
;
417 lif
->lif_sock_ip_fd
= -1;
418 lif
->lif_packet_id
= -1;
419 lif
->lif_iaid_id
= -1;
420 lif
->lif_hold_count
= 1;
422 lif
->lif_removed
= B_TRUE
;
423 init_timer(&lif
->lif_preferred
, 0);
424 init_timer(&lif
->lif_expire
, 0);
426 if (strlcpy(lif
->lif_name
, lname
, LIFNAMSIZ
) >= LIFNAMSIZ
) {
427 dhcpmsg(MSG_ERROR
, "insert_lif: interface name %s is too long",
429 *error
= DHCP_IPC_E_INVIF
;
433 (void) strlcpy(lifr
.lifr_name
, lname
, LIFNAMSIZ
);
435 fd
= pif
->pif_isv6
? v6_sock_fd
: v4_sock_fd
;
437 if (ioctl(fd
, SIOCGLIFMTU
, &lifr
) == -1)
440 lif
->lif_max
= lifr
.lifr_mtu
;
442 if (ioctl(fd
, SIOCGLIFADDR
, &lifr
) == -1) {
444 *error
= DHCP_IPC_E_INVIF
;
446 *error
= DHCP_IPC_E_INT
;
447 dhcpmsg(MSG_ERR
, "insert_lif: SIOCGLIFADDR for %s", lname
);
450 ASSIGN_ADDR(lif_addr
, lif_v6addr
, lifr_addr
);
452 if (ioctl(fd
, SIOCGLIFNETMASK
, &lifr
) == -1) {
454 *error
= DHCP_IPC_E_INVIF
;
456 *error
= DHCP_IPC_E_INT
;
457 dhcpmsg(MSG_ERR
, "insert_lif: SIOCGLIFNETMASK for %s", lname
);
460 ASSIGN_ADDR(lif_netmask
, lif_v6mask
, lifr_addr
);
462 if (ioctl(fd
, SIOCGLIFFLAGS
, &lifr
) == -1) {
463 *error
= DHCP_IPC_E_INT
;
464 dhcpmsg(MSG_ERR
, "insert_lif: SIOCGLIFFLAGS for %s", lname
);
467 lif
->lif_flags
= lifr
.lifr_flags
;
470 * If we've just detected the interface going up or down, then signal
471 * an appropriate action. There may be other state machines here.
473 if ((lifr
.lifr_flags
& IFF_RUNNING
) && !pif
->pif_running
) {
474 pif_status(pif
, B_TRUE
);
475 } else if (!(lifr
.lifr_flags
& IFF_RUNNING
) && pif
->pif_running
) {
476 pif_status(pif
, B_FALSE
);
479 if (lifr
.lifr_flags
& IFF_POINTOPOINT
) {
480 if (ioctl(fd
, SIOCGLIFDSTADDR
, &lifr
) == -1) {
481 *error
= DHCP_IPC_E_INT
;
482 dhcpmsg(MSG_ERR
, "insert_lif: SIOCGLIFDSTADDR for %s",
486 ASSIGN_ADDR(lif_peer
, lif_v6peer
, lifr_dstaddr
);
487 } else if (!pif
->pif_isv6
&& (lifr
.lifr_flags
& IFF_BROADCAST
)) {
488 if (ioctl(fd
, SIOCGLIFBRDADDR
, &lifr
) == -1) {
489 *error
= DHCP_IPC_E_INT
;
490 dhcpmsg(MSG_ERR
, "insert_lif: SIOCGLIFBRDADDR for %s",
495 ((struct sockaddr_in
*)&lifr
.lifr_broadaddr
)->sin_addr
.
500 cached_v6_max_mtu
= 0;
502 cached_v4_max_mtu
= 0;
504 lif
->lif_removed
= B_FALSE
;
505 insque(lif
, &pif
->pif_lifs
);
515 * hold_lif(): acquire a hold on a logical interface structure.
517 * input: dhcp_lif_t *: a pointer to the LIF structure
522 hold_lif(dhcp_lif_t
*lif
)
524 lif
->lif_hold_count
++;
525 dhcpmsg(MSG_DEBUG2
, "hold_lif: hold count on %s: %u", lif
->lif_name
,
526 lif
->lif_hold_count
);
530 * release_lif(): release a hold on a logical interface structure; will
531 * destroy the structure on the last hold removed.
533 * input: dhcp_lif_t *: a pointer to the LIF structure
538 release_lif(dhcp_lif_t
*lif
)
540 if (lif
->lif_hold_count
== 0) {
541 dhcpmsg(MSG_CRIT
, "release_lif: extraneous release on %s",
546 if (lif
->lif_hold_count
== 1 && !lif
->lif_removed
) {
551 if (--lif
->lif_hold_count
== 0) {
554 dhcpmsg(MSG_DEBUG
, "release_lif: freeing LIF %s",
557 if (lif
->lif_lease
!= NULL
)
559 "release_lif: still holding lease at last hold!");
563 cached_v6_max_mtu
= 0;
565 cached_v4_max_mtu
= 0;
569 dhcpmsg(MSG_DEBUG2
, "release_lif: hold count on %s: %u",
570 lif
->lif_name
, lif
->lif_hold_count
);
575 * remove_lif(): remove a logical interface from its PIF and lease (if any) and
576 * the lease's hold on the LIF. Assumes that we did not plumb
579 * input: dhcp_lif_t *: a pointer to the LIF structure
584 remove_lif(dhcp_lif_t
*lif
)
586 if (lif
->lif_plumbed
) {
587 dhcpmsg(MSG_CRIT
, "remove_lif: attempted invalid removal of %s",
591 if (lif
->lif_removed
) {
592 dhcpmsg(MSG_CRIT
, "remove_lif: extraneous removal of %s",
598 dhcpmsg(MSG_DEBUG2
, "remove_lif: removing %s", lif
->lif_name
);
599 lif
->lif_removed
= B_TRUE
;
600 lifnext
= lif
->lif_next
;
602 cancel_lif_timers(lif
);
603 if (lif
->lif_iaid_id
!= -1 &&
604 iu_cancel_timer(tq
, lif
->lif_iaid_id
, NULL
) == 1) {
605 lif
->lif_iaid_id
= -1;
609 /* Remove from PIF list */
612 /* If we were part of a lease, then remove ourselves */
613 if ((dlp
= lif
->lif_lease
) != NULL
) {
614 if (--dlp
->dl_nlifs
== 0)
616 else if (dlp
->dl_lifs
== lif
)
617 dlp
->dl_lifs
= lifnext
;
618 if (lif
->lif_declined
!= NULL
) {
619 dlp
->dl_smach
->dsm_lif_down
--;
620 lif
->lif_declined
= NULL
;
622 if (lif
->lif_dad_wait
) {
623 lif
->lif_dad_wait
= _B_FALSE
;
624 dlp
->dl_smach
->dsm_lif_wait
--;
626 lif
->lif_lease
= NULL
;
633 * lookup_lif_by_name(): Looks up a logical interface entry given a name and
634 * a physical interface.
636 * input: const char *: the logical interface name
637 * const dhcp_pif_t *: the physical interface
638 * output: dhcp_lif_t *: the matching LIF, or NULL if not found
642 lookup_lif_by_name(const char *lname
, const dhcp_pif_t
*pif
)
646 for (lif
= pif
->pif_lifs
; lif
!= NULL
; lif
= lif
->lif_next
) {
647 if (strcmp(lif
->lif_name
, lname
) == 0)
655 * checkaddr(): checks if the given address is still set on the given LIF
657 * input: const dhcp_lif_t *: the LIF to check
658 * int: the address to look up on the interface (ioctl)
659 * const in6_addr_t *: the address to compare to
660 * const char *: name of the address for logging purposes
661 * output: boolean_t: B_TRUE if the address is still set; B_FALSE if not
665 checkaddr(const dhcp_lif_t
*lif
, int ioccmd
, const in6_addr_t
*addr
,
671 char abuf1
[INET6_ADDRSTRLEN
];
672 char abuf2
[INET6_ADDRSTRLEN
];
674 (void) memset(&lifr
, 0, sizeof (struct lifreq
));
675 (void) strlcpy(lifr
.lifr_name
, lif
->lif_name
, LIFNAMSIZ
);
677 isv6
= lif
->lif_pif
->pif_isv6
;
678 fd
= isv6
? v6_sock_fd
: v4_sock_fd
;
680 if (ioctl(fd
, ioccmd
, &lifr
) == -1) {
681 if (errno
== ENXIO
) {
682 dhcpmsg(MSG_WARNING
, "checkaddr: interface %s is gone",
687 "checkaddr: ignoring ioctl error on %s %x: %s",
688 lif
->lif_name
, ioccmd
, strerror(errno
));
690 struct sockaddr_in6
*sin6
=
691 (struct sockaddr_in6
*)&lifr
.lifr_addr
;
693 if (!IN6_ARE_ADDR_EQUAL(&sin6
->sin6_addr
, addr
)) {
695 "checkaddr: expected %s %s on %s, have %s", aname
,
696 inet_ntop(AF_INET6
, addr
, abuf1
, sizeof (abuf1
)),
697 lif
->lif_name
, inet_ntop(AF_INET6
, &sin6
->sin6_addr
,
698 abuf2
, sizeof (abuf2
)));
702 struct sockaddr_in
*sinp
=
703 (struct sockaddr_in
*)&lifr
.lifr_addr
;
706 IN6_V4MAPPED_TO_IPADDR(addr
, v4addr
);
707 if (sinp
->sin_addr
.s_addr
!= v4addr
) {
709 "checkaddr: expected %s %s on %s, have %s", aname
,
710 inet_ntop(AF_INET
, &v4addr
, abuf1
, sizeof (abuf1
)),
711 lif
->lif_name
, inet_ntop(AF_INET
, &sinp
->sin_addr
,
712 abuf2
, sizeof (abuf2
)));
720 * verify_lif(): verifies than a LIF is still valid (i.e., has not been
721 * explicitly or implicitly dropped or released)
723 * input: const dhcp_lif_t *: the LIF to verify
724 * output: boolean_t: B_TRUE if the LIF is still valid, B_FALSE otherwise
728 verify_lif(const dhcp_lif_t
*lif
)
733 dhcp_pif_t
*pif
= lif
->lif_pif
;
735 (void) memset(&lifr
, 0, sizeof (struct lifreq
));
736 (void) strlcpy(lifr
.lifr_name
, lif
->lif_name
, LIFNAMSIZ
);
738 isv6
= pif
->pif_isv6
;
739 fd
= isv6
? v6_sock_fd
: v4_sock_fd
;
741 if (ioctl(fd
, SIOCGLIFFLAGS
, &lifr
) == -1) {
742 if (errno
!= ENXIO
) {
744 "verify_lif: SIOCGLIFFLAGS failed on %s",
751 * If important flags have changed, then abandon the interface.
753 if ((lif
->lif_flags
^ lifr
.lifr_flags
) & DHCP_IFF_WATCH
) {
754 dhcpmsg(MSG_DEBUG
, "verify_lif: unexpected flag change on %s: "
755 "%llx to %llx (%llx)", lif
->lif_name
, lif
->lif_flags
,
756 lifr
.lifr_flags
, (lif
->lif_flags
^ lifr
.lifr_flags
) &
762 * Check for delete and recreate.
764 if (ioctl(fd
, SIOCGLIFINDEX
, &lifr
) == -1) {
765 if (errno
!= ENXIO
) {
766 dhcpmsg(MSG_ERR
, "verify_lif: SIOCGLIFINDEX failed "
767 "on %s", lif
->lif_name
);
771 if (lifr
.lifr_index
!= pif
->pif_index
) {
773 "verify_lif: ifindex on %s changed: %u to %u",
774 lif
->lif_name
, pif
->pif_index
, lifr
.lifr_index
);
778 if (pif
->pif_under_ipmp
) {
779 (void) strlcpy(lifr
.lifr_name
, pif
->pif_grifname
, LIFNAMSIZ
);
781 if (ioctl(fd
, SIOCGLIFINDEX
, &lifr
) == -1) {
782 if (errno
!= ENXIO
) {
783 dhcpmsg(MSG_ERR
, "verify_lif: SIOCGLIFINDEX "
784 "failed on %s", lifr
.lifr_name
);
789 if (lifr
.lifr_index
!= pif
->pif_grindex
) {
790 dhcpmsg(MSG_DEBUG
, "verify_lif: IPMP group ifindex "
791 "on %s changed: %u to %u", lifr
.lifr_name
,
792 pif
->pif_grindex
, lifr
.lifr_index
);
798 * If the IP address, netmask, or broadcast address have changed, or
799 * the interface has been unplumbed, then we act like there has been an
800 * implicit drop. (Note that the netmask is under DHCP control for
801 * IPv4, but not for DHCPv6, and that IPv6 doesn't have broadcast
805 if (!checkaddr(lif
, SIOCGLIFADDR
, &lif
->lif_v6addr
, "local address"))
810 * If it's not point-to-point, we're done. If it is, then
811 * check the peer's address as well.
813 return (!(lif
->lif_flags
& IFF_POINTOPOINT
) ||
814 checkaddr(lif
, SIOCGLIFDSTADDR
, &lif
->lif_v6peer
,
817 if (!checkaddr(lif
, SIOCGLIFNETMASK
, &lif
->lif_v6mask
,
821 return (checkaddr(lif
,
822 (lif
->lif_flags
& IFF_POINTOPOINT
) ? SIOCGLIFDSTADDR
:
823 SIOCGLIFBRDADDR
, &lif
->lif_v6peer
, "peer address"));
828 * canonize_lif(): puts the interface in a canonical (zeroed) form. This is
829 * used only on the "main" LIF for IPv4. All other interfaces
830 * are under dhcpagent control and are removed using
833 * input: dhcp_lif_t *: the interface to canonize
834 * boolean_t: only canonize lif if it's under DHCP control
839 canonize_lif(dhcp_lif_t
*lif
, boolean_t dhcponly
)
846 * If there's nothing here, then don't touch the interface. This can
847 * happen when an already-canonized LIF is recanonized.
849 if (IN6_IS_ADDR_UNSPECIFIED(&lif
->lif_v6addr
))
852 isv6
= lif
->lif_pif
->pif_isv6
;
853 dhcpmsg(MSG_VERBOSE
, "canonizing IPv%d interface %s",
854 isv6
? 6 : 4, lif
->lif_name
);
856 lif
->lif_v6addr
= in6addr_any
;
857 lif
->lif_v6mask
= in6addr_any
;
858 lif
->lif_v6peer
= in6addr_any
;
860 (void) memset(&lifr
, 0, sizeof (struct lifreq
));
861 (void) strlcpy(lifr
.lifr_name
, lif
->lif_name
, LIFNAMSIZ
);
863 fd
= isv6
? v6_sock_fd
: v4_sock_fd
;
865 if (ioctl(fd
, SIOCGLIFFLAGS
, &lifr
) == -1) {
866 if (errno
!= ENXIO
) {
867 dhcpmsg(MSG_ERR
, "canonize_lif: can't get flags for %s",
872 lif
->lif_flags
= lifr
.lifr_flags
;
874 if (dhcponly
&& !(lifr
.lifr_flags
& IFF_DHCPRUNNING
)) {
876 "canonize_lif: cannot clear %s; flags are %llx",
877 lif
->lif_name
, lifr
.lifr_flags
);
881 (void) memset(&lifr
.lifr_addr
, 0, sizeof (lifr
.lifr_addr
));
883 struct sockaddr_in6
*sin6
=
884 (struct sockaddr_in6
*)&lifr
.lifr_addr
;
886 sin6
->sin6_family
= AF_INET6
;
887 sin6
->sin6_addr
= in6addr_any
;
889 struct sockaddr_in
*sinv
=
890 (struct sockaddr_in
*)&lifr
.lifr_addr
;
892 sinv
->sin_family
= AF_INET
;
893 sinv
->sin_addr
.s_addr
= htonl(INADDR_ANY
);
896 if (ioctl(fd
, SIOCSLIFADDR
, &lifr
) == -1) {
898 "canonize_lif: can't clear local address on %s",
902 /* Clearing the address means that we're no longer waiting on DAD */
903 if (lif
->lif_dad_wait
) {
904 lif
->lif_dad_wait
= _B_FALSE
;
905 lif
->lif_lease
->dl_smach
->dsm_lif_wait
--;
908 if (lif
->lif_flags
& IFF_POINTOPOINT
) {
909 if (ioctl(fd
, SIOCSLIFDSTADDR
, &lifr
) == -1) {
911 "canonize_lif: can't clear remote address on %s",
915 if (ioctl(fd
, SIOCSLIFBRDADDR
, &lifr
) == -1) {
917 "canonize_lif: can't clear broadcast address on %s",
923 * Clear the netmask last as it has to be refetched after clearing.
924 * Netmask is under in.ndpd control with IPv6.
927 /* Clear the netmask */
928 if (ioctl(fd
, SIOCSLIFNETMASK
, &lifr
) == -1) {
930 "canonize_lif: can't clear netmask on %s",
934 * When the netmask is cleared, the kernel actually sets
935 * the netmask to 255.0.0.0. So, refetch that netmask.
937 if (ioctl(fd
, SIOCGLIFNETMASK
, &lifr
) == -1) {
939 "canonize_lif: can't reload cleared "
940 "netmask on %s", lif
->lif_name
);
942 /* Refetch succeeded, update LIF */
944 ((struct sockaddr_in
*)&lifr
.lifr_addr
)->
952 * plumb_lif(): Adds the LIF to the system. This is used for all
953 * DHCPv6-derived interfaces. The returned LIF has a hold
954 * on it. The caller (configure_v6_leases) deals with the DAD
957 * input: dhcp_lif_t *: the interface to unplumb
962 plumb_lif(dhcp_pif_t
*pif
, const in6_addr_t
*addr
)
965 char abuf
[INET6_ADDRSTRLEN
];
967 struct sockaddr_in6
*sin6
;
970 (void) inet_ntop(AF_INET6
, addr
, abuf
, sizeof (abuf
));
972 for (lif
= pif
->pif_lifs
; lif
!= NULL
; lif
= lif
->lif_next
) {
973 if (IN6_ARE_ADDR_EQUAL(&lif
->lif_v6addr
, addr
)) {
975 "plumb_lif: entry for %s already exists!", abuf
);
980 /* First, create a new zero-address logical interface */
981 (void) memset(&lifr
, 0, sizeof (lifr
));
982 (void) strlcpy(lifr
.lifr_name
, pif
->pif_name
, sizeof (lifr
.lifr_name
));
983 if (ioctl(v6_sock_fd
, SIOCLIFADDIF
, &lifr
) == -1) {
984 dhcpmsg(MSG_ERR
, "plumb_lif: SIOCLIFADDIF %s", pif
->pif_name
);
988 /* Next, set the netmask to all ones */
989 sin6
= (struct sockaddr_in6
*)&lifr
.lifr_addr
;
990 sin6
->sin6_family
= AF_INET6
;
991 (void) memset(&sin6
->sin6_addr
, 0xff, sizeof (sin6
->sin6_addr
));
992 if (ioctl(v6_sock_fd
, SIOCSLIFNETMASK
, &lifr
) == -1) {
993 dhcpmsg(MSG_ERR
, "plumb_lif: SIOCSLIFNETMASK %s",
998 /* Now set the interface address */
999 sin6
->sin6_addr
= *addr
;
1000 if (ioctl(v6_sock_fd
, SIOCSLIFADDR
, &lifr
) == -1) {
1001 dhcpmsg(MSG_ERR
, "plumb_lif: SIOCSLIFADDR %s %s",
1002 lifr
.lifr_name
, abuf
);
1006 /* Mark the interface up */
1007 if (ioctl(v6_sock_fd
, SIOCGLIFFLAGS
, &lifr
) == -1) {
1008 dhcpmsg(MSG_ERR
, "plumb_lif: SIOCGLIFFLAGS %s",
1014 * See comment in set_lif_dhcp().
1016 if (pif
->pif_under_ipmp
&& !(lifr
.lifr_flags
& IFF_NOFAILOVER
))
1017 lifr
.lifr_flags
|= IFF_NOFAILOVER
| IFF_DEPRECATED
;
1019 lifr
.lifr_flags
|= IFF_UP
| IFF_DHCPRUNNING
;
1020 if (ioctl(v6_sock_fd
, SIOCSLIFFLAGS
, &lifr
) == -1) {
1021 dhcpmsg(MSG_ERR
, "plumb_lif: SIOCSLIFFLAGS %s",
1026 /* Now we can create the internal LIF structure */
1028 if ((lif
= insert_lif(pif
, lifr
.lifr_name
, &error
)) == NULL
)
1031 dhcpmsg(MSG_DEBUG
, "plumb_lif: plumbed up %s on %s", abuf
,
1033 lif
->lif_plumbed
= B_TRUE
;
1038 if (ioctl(v6_sock_fd
, SIOCLIFREMOVEIF
, &lifr
) == -1 &&
1040 dhcpmsg(MSG_ERR
, "plumb_lif: SIOCLIFREMOVEIF %s",
1047 * unplumb_lif(): Removes the LIF from dhcpagent and the system. This is used
1048 * for all interfaces configured by DHCP (those in leases).
1050 * input: dhcp_lif_t *: the interface to unplumb
1055 unplumb_lif(dhcp_lif_t
*lif
)
1059 if (lif
->lif_plumbed
) {
1062 (void) memset(&lifr
, 0, sizeof (lifr
));
1063 (void) strlcpy(lifr
.lifr_name
, lif
->lif_name
,
1064 sizeof (lifr
.lifr_name
));
1065 if (ioctl(v6_sock_fd
, SIOCLIFREMOVEIF
, &lifr
) == -1 &&
1067 dhcpmsg(MSG_ERR
, "unplumb_lif: SIOCLIFREMOVEIF %s",
1070 lif
->lif_plumbed
= B_FALSE
;
1074 * Special case: if we're "unplumbing" the main LIF for DHCPv4, then
1075 * just canonize it and remove it from the lease. The DAD wait flags
1076 * are handled by canonize_lif or by remove_lif.
1078 if ((dlp
= lif
->lif_lease
) != NULL
&& dlp
->dl_smach
->dsm_lif
== lif
) {
1079 canonize_lif(lif
, B_TRUE
);
1080 cancel_lif_timers(lif
);
1081 if (lif
->lif_declined
!= NULL
) {
1082 dlp
->dl_smach
->dsm_lif_down
--;
1083 lif
->lif_declined
= NULL
;
1086 dlp
->dl_lifs
= NULL
;
1087 lif
->lif_lease
= NULL
;
1095 * attach_lif(): create a new logical interface, creating the physical
1096 * interface as necessary.
1098 * input: const char *: the logical interface name
1099 * boolean_t: B_TRUE for IPv6
1100 * int *: set to DHCP_IPC_E_* if creation fails
1101 * output: dhcp_lif_t *: pointer to new entry, or NULL on failure
1105 attach_lif(const char *lname
, boolean_t isv6
, int *error
)
1108 char pname
[LIFNAMSIZ
], *cp
;
1110 (void) strlcpy(pname
, lname
, sizeof (pname
));
1111 if ((cp
= strchr(pname
, ':')) != NULL
)
1114 if ((pif
= lookup_pif_by_name(pname
, isv6
)) != NULL
)
1116 else if ((pif
= insert_pif(pname
, isv6
, error
)) == NULL
)
1119 if (lookup_lif_by_name(lname
, pif
) != NULL
) {
1120 dhcpmsg(MSG_ERROR
, "attach_lif: entry for %s already exists!",
1123 *error
= DHCP_IPC_E_INVIF
;
1127 /* If LIF creation fails, then insert_lif discards our PIF hold */
1128 return (insert_lif(pif
, lname
, error
));
1132 * set_lif_dhcp(): Set logical interface flags to show that it's managed
1135 * input: dhcp_lif_t *: the logical interface
1136 * output: int: set to DHCP_IPC_E_* if operation fails
1140 set_lif_dhcp(dhcp_lif_t
*lif
)
1145 dhcp_pif_t
*pif
= lif
->lif_pif
;
1147 fd
= pif
->pif_isv6
? v6_sock_fd
: v4_sock_fd
;
1149 (void) strlcpy(lifr
.lifr_name
, lif
->lif_name
, LIFNAMSIZ
);
1151 if (ioctl(fd
, SIOCGLIFFLAGS
, &lifr
) == -1) {
1153 dhcpmsg(MSG_ERR
, "set_lif_dhcp: SIOCGLIFFLAGS for %s",
1155 return (err
== ENXIO
? DHCP_IPC_E_INVIF
: DHCP_IPC_E_INT
);
1157 lif
->lif_flags
= lifr
.lifr_flags
;
1160 * Check for conflicting sources of address control, and other
1161 * unacceptable configurations.
1163 if (lifr
.lifr_flags
& (IFF_LOOPBACK
|IFF_ADDRCONF
|IFF_TEMPORARY
|
1165 dhcpmsg(MSG_ERR
, "set_lif_dhcp: cannot use %s: flags are %llx",
1166 lif
->lif_name
, lifr
.lifr_flags
);
1167 return (DHCP_IPC_E_INVIF
);
1171 * If IFF_DHCPRUNNING is already set on the interface and we're not
1172 * adopting it, the agent probably crashed and burned. Note it, but
1173 * don't let it stop the proceedings (we're pretty sure we're not
1174 * already running, since we were able to bind to our IPC port).
1176 if (lifr
.lifr_flags
& IFF_DHCPRUNNING
) {
1177 dhcpmsg(MSG_VERBOSE
, "set_lif_dhcp: IFF_DHCPRUNNING already set"
1178 " on %s", lif
->lif_name
);
1181 * If the lif is on an interface under IPMP, IFF_NOFAILOVER
1182 * must be set or the kernel will prevent us from setting
1183 * IFF_DHCPRUNNING (since the subsequent IFF_UP would lead to
1184 * migration). We set IFF_DEPRECATED too since the kernel
1185 * will set it automatically when setting IFF_NOFAILOVER,
1186 * causing our lif_flags value to grow stale.
1188 if (pif
->pif_under_ipmp
&& !(lifr
.lifr_flags
& IFF_NOFAILOVER
))
1189 lifr
.lifr_flags
|= IFF_NOFAILOVER
| IFF_DEPRECATED
;
1191 lifr
.lifr_flags
|= IFF_DHCPRUNNING
;
1192 if (ioctl(fd
, SIOCSLIFFLAGS
, &lifr
) == -1) {
1193 dhcpmsg(MSG_ERR
, "set_lif_dhcp: SIOCSLIFFLAGS for %s",
1195 return (DHCP_IPC_E_INT
);
1197 lif
->lif_flags
= lifr
.lifr_flags
;
1199 return (DHCP_IPC_SUCCESS
);
1203 * clear_lif_dhcp(): Clear logical interface flags to show that it's no longer
1206 * input: dhcp_lif_t *: the logical interface
1211 clear_lif_dhcp(dhcp_lif_t
*lif
)
1216 fd
= lif
->lif_pif
->pif_isv6
? v6_sock_fd
: v4_sock_fd
;
1218 (void) strlcpy(lifr
.lifr_name
, lif
->lif_name
, LIFNAMSIZ
);
1220 if (ioctl(fd
, SIOCGLIFFLAGS
, &lifr
) == -1)
1223 if (!(lifr
.lifr_flags
& IFF_DHCPRUNNING
))
1226 lif
->lif_flags
= lifr
.lifr_flags
&= ~IFF_DHCPRUNNING
;
1227 (void) ioctl(fd
, SIOCSLIFFLAGS
, &lifr
);
1231 * set_lif_deprecated(): Set the "deprecated" flag to tell users that this
1232 * address will be going away. As the interface is
1233 * going away, we don't care if there are errors.
1235 * input: dhcp_lif_t *: the logical interface
1240 set_lif_deprecated(dhcp_lif_t
*lif
)
1245 if (lif
->lif_flags
& IFF_DEPRECATED
)
1248 fd
= lif
->lif_pif
->pif_isv6
? v6_sock_fd
: v4_sock_fd
;
1250 (void) strlcpy(lifr
.lifr_name
, lif
->lif_name
, LIFNAMSIZ
);
1252 if (ioctl(fd
, SIOCGLIFFLAGS
, &lifr
) == -1)
1255 if (lifr
.lifr_flags
& IFF_DEPRECATED
)
1258 lifr
.lifr_flags
|= IFF_DEPRECATED
;
1259 (void) ioctl(fd
, SIOCSLIFFLAGS
, &lifr
);
1260 lif
->lif_flags
= lifr
.lifr_flags
;
1264 * clear_lif_deprecated(): Clear the "deprecated" flag to tell users that this
1265 * address will not be going away. This happens if we
1266 * get a renewal after preferred lifetime but before
1267 * the valid lifetime.
1269 * input: dhcp_lif_t *: the logical interface
1270 * output: boolean_t: B_TRUE on success.
1274 clear_lif_deprecated(dhcp_lif_t
*lif
)
1279 fd
= lif
->lif_pif
->pif_isv6
? v6_sock_fd
: v4_sock_fd
;
1281 (void) strlcpy(lifr
.lifr_name
, lif
->lif_name
, LIFNAMSIZ
);
1283 if (ioctl(fd
, SIOCGLIFFLAGS
, &lifr
) == -1) {
1284 dhcpmsg(MSG_ERR
, "clear_lif_deprecated: SIOCGLIFFLAGS for %s",
1290 * Check for conflicting sources of address control, and other
1291 * unacceptable configurations.
1293 if (lifr
.lifr_flags
& (IFF_LOOPBACK
|IFF_ADDRCONF
|IFF_TEMPORARY
|
1295 dhcpmsg(MSG_ERR
, "clear_lif_deprecated: cannot use %s: flags "
1296 "are %llx", lif
->lif_name
, lifr
.lifr_flags
);
1301 * Don't try to clear IFF_DEPRECATED if this is a test address,
1302 * since IPMP's use of IFF_DEPRECATED is not compatible with ours.
1304 if (lifr
.lifr_flags
& IFF_NOFAILOVER
)
1307 if (!(lifr
.lifr_flags
& IFF_DEPRECATED
))
1310 lifr
.lifr_flags
&= ~IFF_DEPRECATED
;
1311 if (ioctl(fd
, SIOCSLIFFLAGS
, &lifr
) == -1) {
1312 dhcpmsg(MSG_ERR
, "clear_lif_deprecated: SIOCSLIFFLAGS for %s",
1316 lif
->lif_flags
= lifr
.lifr_flags
;
1322 * open_ip_lif(): open up an IP socket for I/O on a given LIF (v4 only).
1324 * input: dhcp_lif_t *: the logical interface to operate on
1325 * in_addr_t: the address the socket will be bound to (in hbo)
1326 * boolean_t: B_TRUE if the address should be brought up (if needed)
1327 * output: boolean_t: B_TRUE if the socket was opened successfully.
1331 open_ip_lif(dhcp_lif_t
*lif
, in_addr_t addr_hbo
, boolean_t bringup
)
1338 dhcp_pif_t
*pif
= lif
->lif_pif
;
1340 if (lif
->lif_sock_ip_fd
!= -1) {
1341 dhcpmsg(MSG_WARNING
, "open_ip_lif: socket already open on %s",
1346 lif
->lif_sock_ip_fd
= socket(AF_INET
, SOCK_DGRAM
, 0);
1347 if (lif
->lif_sock_ip_fd
== -1) {
1348 errmsg
= "cannot create v4 socket";
1352 if (!bind_sock(lif
->lif_sock_ip_fd
, IPPORT_BOOTPC
, addr_hbo
)) {
1353 errmsg
= "cannot bind v4 socket";
1358 * If we bound to INADDR_ANY, we have no IFF_UP source address to use.
1359 * Thus, enable IP_UNSPEC_SRC so that we can send packets with an
1360 * unspecified (0.0.0.0) address. Also, enable IP_DHCPINIT_IF so that
1361 * the IP module will accept unicast DHCP traffic regardless of the IP
1362 * address it's sent to. (We'll then figure out which packets are
1363 * ours based on the xid.)
1365 if (addr_hbo
== INADDR_ANY
) {
1366 if (setsockopt(lif
->lif_sock_ip_fd
, IPPROTO_IP
, IP_UNSPEC_SRC
,
1367 &on
, sizeof (int)) == -1) {
1368 errmsg
= "cannot set IP_UNSPEC_SRC";
1372 if (setsockopt(lif
->lif_sock_ip_fd
, IPPROTO_IP
, IP_DHCPINIT_IF
,
1373 &pif
->pif_index
, sizeof (int)) == -1) {
1374 errmsg
= "cannot set IP_DHCPINIT_IF";
1380 * Unfortunately, some hardware (such as the Linksys WRT54GC)
1381 * decrements the TTL *prior* to accepting DHCP traffic destined
1382 * for it. To workaround this, tell IP to use a TTL of 255 for
1383 * broadcast packets sent from this socket.
1385 if (setsockopt(lif
->lif_sock_ip_fd
, IPPROTO_IP
, IP_BROADCAST_TTL
, &ttl
,
1386 sizeof (uchar_t
)) == -1) {
1387 errmsg
= "cannot set IP_BROADCAST_TTL";
1391 ifindex
= pif
->pif_under_ipmp
? pif
->pif_grindex
: pif
->pif_index
;
1392 if (setsockopt(lif
->lif_sock_ip_fd
, IPPROTO_IP
, IP_BOUND_IF
, &ifindex
,
1393 sizeof (int)) == -1) {
1394 errmsg
= "cannot set IP_BOUND_IF";
1398 (void) strlcpy(lifr
.lifr_name
, lif
->lif_name
, LIFNAMSIZ
);
1399 if (ioctl(v4_sock_fd
, SIOCGLIFFLAGS
, &lifr
) == -1) {
1400 errmsg
= "cannot get interface flags";
1405 * If the lif is part of an interface under IPMP, IFF_NOFAILOVER must
1406 * be set or the kernel will prevent us from setting IFF_DHCPRUNNING
1407 * (since the subsequent IFF_UP would lead to migration). We set
1408 * IFF_DEPRECATED too since the kernel will set it automatically when
1409 * setting IFF_NOFAILOVER, causing our lif_flags value to grow stale.
1411 if (pif
->pif_under_ipmp
&& !(lifr
.lifr_flags
& IFF_NOFAILOVER
)) {
1412 lifr
.lifr_flags
|= IFF_NOFAILOVER
| IFF_DEPRECATED
;
1413 if (ioctl(v4_sock_fd
, SIOCSLIFFLAGS
, &lifr
) == -1) {
1414 errmsg
= "cannot set IFF_NOFAILOVER";
1418 lif
->lif_flags
= lifr
.lifr_flags
;
1421 * If this is initial bringup, make sure the address we're acquiring a
1422 * lease on is IFF_UP.
1424 if (bringup
&& !(lifr
.lifr_flags
& IFF_UP
)) {
1426 * Start from a clean slate.
1428 canonize_lif(lif
, B_FALSE
);
1430 lifr
.lifr_flags
|= IFF_UP
;
1431 if (ioctl(v4_sock_fd
, SIOCSLIFFLAGS
, &lifr
) == -1) {
1432 errmsg
= "cannot bring up";
1435 lif
->lif_flags
= lifr
.lifr_flags
;
1438 * When bringing 0.0.0.0 IFF_UP, the kernel changes the
1439 * netmask to 255.0.0.0, so re-fetch our expected netmask.
1441 if (ioctl(v4_sock_fd
, SIOCGLIFNETMASK
, &lifr
) == -1) {
1442 errmsg
= "cannot get netmask";
1447 ((struct sockaddr_in
*)&lifr
.lifr_addr
)->sin_addr
.s_addr
;
1451 * Usually, bringing up the address we're acquiring a lease on is
1452 * sufficient to allow packets to be sent and received via the
1453 * IP_BOUND_IF we did earlier. However, if we're acquiring a lease on
1454 * an underlying IPMP interface, the group interface will be used for
1455 * sending and receiving IP packets via IP_BOUND_IF. Thus, ensure at
1456 * least one address on the group interface is IFF_UP.
1458 if (bringup
&& pif
->pif_under_ipmp
) {
1459 (void) strlcpy(lifr
.lifr_name
, pif
->pif_grifname
, LIFNAMSIZ
);
1460 if (ioctl(v4_sock_fd
, SIOCGLIFFLAGS
, &lifr
) == -1) {
1461 errmsg
= "cannot get IPMP group interface flags";
1465 if (!(lifr
.lifr_flags
& IFF_UP
)) {
1466 lifr
.lifr_flags
|= IFF_UP
;
1467 if (ioctl(v4_sock_fd
, SIOCSLIFFLAGS
, &lifr
) == -1) {
1468 errmsg
= "cannot bring up IPMP group interface";
1474 lif
->lif_packet_id
= iu_register_event(eh
, lif
->lif_sock_ip_fd
, POLLIN
,
1475 dhcp_packet_lif
, lif
);
1476 if (lif
->lif_packet_id
== -1) {
1477 errmsg
= "cannot register to receive DHCP packets";
1483 dhcpmsg(MSG_ERR
, "open_ip_lif: %s: %s", lif
->lif_name
, errmsg
);
1489 * close_ip_lif(): close an IP socket for I/O on a given LIF.
1491 * input: dhcp_lif_t *: the logical interface to operate on
1496 close_ip_lif(dhcp_lif_t
*lif
)
1498 if (lif
->lif_packet_id
!= -1) {
1499 (void) iu_unregister_event(eh
, lif
->lif_packet_id
, NULL
);
1500 lif
->lif_packet_id
= -1;
1502 if (lif
->lif_sock_ip_fd
!= -1) {
1503 (void) close(lif
->lif_sock_ip_fd
);
1504 lif
->lif_sock_ip_fd
= -1;
1509 * lif_mark_decline(): mark a LIF as having been declined due to a duplicate
1510 * address or some other conflict. This is used in
1511 * send_declines() to report failure back to the server.
1513 * input: dhcp_lif_t *: the logical interface to operate on
1514 * const char *: text string explaining why the address is declined
1519 lif_mark_decline(dhcp_lif_t
*lif
, const char *reason
)
1521 if (lif
->lif_declined
== NULL
) {
1524 lif
->lif_declined
= reason
;
1525 if ((dlp
= lif
->lif_lease
) != NULL
)
1526 dlp
->dl_smach
->dsm_lif_down
++;
1531 * schedule_lif_timer(): schedules the LIF-related timer
1533 * input: dhcp_lif_t *: the logical interface to operate on
1534 * dhcp_timer_t *: the timer to schedule
1535 * iu_tq_callback_t *: the callback to call upon firing
1536 * output: boolean_t: B_TRUE if the timer was scheduled successfully
1540 schedule_lif_timer(dhcp_lif_t
*lif
, dhcp_timer_t
*dt
, iu_tq_callback_t
*expire
)
1543 * If there's a timer running, cancel it and release its lease
1546 if (dt
->dt_id
!= -1) {
1547 if (!cancel_timer(dt
))
1552 if (schedule_timer(dt
, expire
, lif
)) {
1556 dhcpmsg(MSG_WARNING
,
1557 "schedule_lif_timer: cannot schedule timer");
1563 * cancel_lif_timer(): cancels a LIF-related timer
1565 * input: dhcp_lif_t *: the logical interface to operate on
1566 * dhcp_timer_t *: the timer to cancel
1571 cancel_lif_timer(dhcp_lif_t
*lif
, dhcp_timer_t
*dt
)
1573 if (dt
->dt_id
== -1)
1575 if (cancel_timer(dt
)) {
1577 "cancel_lif_timer: canceled expiry timer on %s",
1581 dhcpmsg(MSG_WARNING
,
1582 "cancel_lif_timer: cannot cancel timer on %s",
1588 * cancel_lif_timers(): cancels the LIF-related timers
1590 * input: dhcp_lif_t *: the logical interface to operate on
1595 cancel_lif_timers(dhcp_lif_t
*lif
)
1597 cancel_lif_timer(lif
, &lif
->lif_preferred
);
1598 cancel_lif_timer(lif
, &lif
->lif_expire
);
1602 * get_max_mtu(): find the maximum MTU of all interfaces for I/O on common
1603 * file descriptors (v4_sock_fd and v6_sock_fd).
1605 * input: boolean_t: B_TRUE for IPv6, B_FALSE for IPv4
1610 get_max_mtu(boolean_t isv6
)
1612 uint_t
*mtup
= isv6
? &cached_v6_max_mtu
: &cached_v4_max_mtu
;
1619 /* Set an arbitrary lower bound */
1621 pif
= isv6
? v6root
: v4root
;
1622 for (; pif
!= NULL
; pif
= pif
->pif_next
) {
1623 for (lif
= pif
->pif_lifs
; lif
!= NULL
;
1624 lif
= lif
->lif_next
) {
1625 (void) strlcpy(lifr
.lifr_name
, lif
->lif_name
,
1627 if (ioctl(v4_sock_fd
, SIOCGLIFMTU
, &lifr
) !=
1628 -1 && lifr
.lifr_mtu
> *mtup
) {
1629 *mtup
= lifr
.lifr_mtu
;
1638 * expired_lif_state(): summarize the state of expired LIFs on a given state
1641 * input: dhcp_smach_t *: the state machine to scan
1642 * output: dhcp_expire_t: overall state
1646 expired_lif_state(dhcp_smach_t
*dsmp
)
1654 numlifs
= numexp
= 0;
1655 for (dlp
= dsmp
->dsm_leases
; dlp
!= NULL
; dlp
= dlp
->dl_next
) {
1657 nlifs
= dlp
->dl_nlifs
;
1659 for (; nlifs
> 0; nlifs
--, lif
= lif
->lif_next
) {
1660 if (lif
->lif_expired
)
1665 return (DHCP_EXP_NOLIFS
);
1666 else if (numexp
== 0)
1667 return (DHCP_EXP_NOEXP
);
1668 else if (numlifs
== numexp
)
1669 return (DHCP_EXP_ALLEXP
);
1671 return (DHCP_EXP_SOMEEXP
);
1675 * find_expired_lif(): find the first expired LIF on a given state machine
1677 * input: dhcp_smach_t *: the state machine to scan
1678 * output: dhcp_lif_t *: the first expired LIF, or NULL if none.
1682 find_expired_lif(dhcp_smach_t
*dsmp
)
1688 for (dlp
= dsmp
->dsm_leases
; dlp
!= NULL
; dlp
= dlp
->dl_next
) {
1690 nlifs
= dlp
->dl_nlifs
;
1691 for (; nlifs
> 0; nlifs
--, lif
= lif
->lif_next
) {
1692 if (lif
->lif_expired
)
1700 * remove_v6_strays(): remove any stray interfaces marked as DHCPRUNNING. Used
1708 remove_v6_strays(void)
1711 struct lifconf lifc
;
1712 struct lifreq
*lifrp
, *lifrmax
;
1717 * Get the approximate number of interfaces in the system. It's only
1718 * approximate because the system is dynamic -- interfaces may be
1719 * plumbed or unplumbed at any time. This is also the reason for the
1720 * "+ 10" fudge factor: we're trying to avoid unnecessary looping.
1722 (void) memset(&lifn
, 0, sizeof (lifn
));
1723 lifn
.lifn_family
= AF_INET6
;
1724 lifn
.lifn_flags
= LIFC_ALLZONES
| LIFC_NOXMIT
| LIFC_TEMPORARY
;
1725 if (ioctl(v6_sock_fd
, SIOCGLIFNUM
, &lifn
) == -1) {
1727 "remove_v6_strays: cannot read number of interfaces");
1730 numifs
= lifn
.lifn_count
+ 10;
1734 * Get the interface information. We do this in a loop so that we can
1735 * recover from EINVAL from the kernel -- delivered when the buffer is
1738 (void) memset(&lifc
, 0, sizeof (lifc
));
1739 lifc
.lifc_family
= AF_INET6
;
1740 lifc
.lifc_flags
= LIFC_ALLZONES
| LIFC_NOXMIT
| LIFC_TEMPORARY
;
1742 lifc
.lifc_len
= numifs
* sizeof (*lifrp
);
1743 lifrp
= realloc(lifc
.lifc_buf
, lifc
.lifc_len
);
1744 if (lifrp
== NULL
) {
1746 "remove_v6_strays: cannot allocate memory");
1747 free(lifc
.lifc_buf
);
1750 lifc
.lifc_buf
= (caddr_t
)lifrp
;
1752 if (ioctl(v6_sock_fd
, SIOCGLIFCONF
, &lifc
) == 0 &&
1753 lifc
.lifc_len
< numifs
* sizeof (*lifrp
))
1755 if (errno
== 0 || errno
== EINVAL
) {
1758 dhcpmsg(MSG_ERR
, "remove_v6_strays: SIOCGLIFCONF");
1759 free(lifc
.lifc_buf
);
1764 lifrmax
= lifrp
+ lifc
.lifc_len
/ sizeof (*lifrp
);
1765 for (; lifrp
< lifrmax
; lifrp
++) {
1767 * Get the interface flags; we're interested in the DHCP ones.
1769 if (ioctl(v6_sock_fd
, SIOCGLIFFLAGS
, lifrp
) == -1)
1771 flags
= lifrp
->lifr_flags
;
1772 if (!(flags
& IFF_DHCPRUNNING
))
1775 * If the interface has a link-local address, then we don't
1776 * control it. Just remove the flag.
1778 if (ioctl(v6_sock_fd
, SIOCGLIFADDR
, lifrp
) == -1)
1780 if (IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6
*)&lifrp
->
1781 lifr_addr
)->sin6_addr
)) {
1782 lifrp
->lifr_flags
= flags
& ~IFF_DHCPRUNNING
;
1783 (void) ioctl(v6_sock_fd
, SIOCSLIFFLAGS
, lifrp
);
1787 * All others are (or were) under our control. Clean up by
1790 if (ioctl(v6_sock_fd
, SIOCLIFREMOVEIF
, lifrp
) == 0) {
1791 dhcpmsg(MSG_DEBUG
, "remove_v6_strays: removed %s",
1793 } else if (errno
!= ENXIO
) {
1795 "remove_v6_strays: SIOCLIFREMOVEIF %s",
1799 free(lifc
.lifc_buf
);