summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c')
-rw-r--r--usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c206
1 files changed, 162 insertions, 44 deletions
diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c
index 0cfdad40e3..5d2d5fb99e 100644
--- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c
+++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/interface.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -76,6 +76,7 @@ insert_pif(const char *pname, boolean_t isv6, int *error)
{
dhcp_pif_t *pif;
struct lifreq lifr;
+ lifgroupinfo_t lifgr;
dlpi_handle_t dh = NULL;
int fd = isv6 ? v6_sock_fd : v4_sock_fd;
@@ -127,12 +128,60 @@ insert_pif(const char *pname, boolean_t isv6, int *error)
}
/*
- * For IPv4, use DLPI to determine the hardware type, hardware
- * address, and hardware address length.
+ * Check if the pif is in an IPMP group. Interfaces using IPMP don't
+ * have dedicated hardware addresses, and get their hardware type from
+ * the SIOCGLIFGROUPINFO ioctl rather than DLPI.
*/
- if (!isv6) {
- int rc;
- dlpi_info_t dlinfo;
+ if (ioctl(fd, SIOCGLIFGROUPNAME, &lifr) == -1) {
+ *error = DHCP_IPC_E_INT;
+ dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFGROUPNAME for %s", pname);
+ goto failure;
+ }
+
+ if (lifr.lifr_groupname[0] != '\0') {
+ (void) strlcpy(lifgr.gi_grname, lifr.lifr_groupname,
+ LIFGRNAMSIZ);
+ if (ioctl(fd, SIOCGLIFGROUPINFO, &lifgr) == -1) {
+ *error = DHCP_IPC_E_INT;
+ dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFGROUPINFO for %s",
+ lifgr.gi_grname);
+ goto failure;
+ }
+
+ pif->pif_hwtype = dlpi_arptype(lifgr.gi_mactype);
+ pif->pif_under_ipmp = (strcmp(pname, lifgr.gi_grifname) != 0);
+ (void) strlcpy(pif->pif_grifname, lifgr.gi_grifname, LIFNAMSIZ);
+
+ /*
+ * For IPMP underlying interfaces, stash the interface index
+ * of the IPMP meta-interface; we'll use it to send/receive
+ * traffic. This is both necessary (since IP_BOUND_IF for
+ * non-unicast traffic won't work on underlying interfaces)
+ * and preferred (since a test address lease will be able to
+ * be maintained as long as another interface in the group is
+ * still functioning).
+ */
+ if (pif->pif_under_ipmp) {
+ (void) strlcpy(lifr.lifr_name, pif->pif_grifname,
+ LIFNAMSIZ);
+
+ if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
+ *error = DHCP_IPC_E_INT;
+ dhcpmsg(MSG_ERR, "insert_pif: SIOCGLIFINDEX "
+ "for %s", lifr.lifr_name);
+ goto failure;
+ }
+ pif->pif_grindex = lifr.lifr_index;
+ }
+ }
+
+ /*
+ * For IPv4, if the hardware type is still unknown, use DLPI to
+ * determine it, the hardware address, and hardware address length.
+ */
+ if (!isv6 && pif->pif_hwtype == 0) {
+ int rc;
+ dlpi_info_t dlinfo;
if ((rc = dlpi_open(pname, &dh, 0)) != DLPI_SUCCESS) {
dhcpmsg(MSG_ERROR, "insert_pif: dlpi_open: %s",
@@ -661,11 +710,12 @@ verify_lif(const dhcp_lif_t *lif)
boolean_t isv6;
int fd;
struct lifreq lifr;
+ dhcp_pif_t *pif = lif->lif_pif;
(void) memset(&lifr, 0, sizeof (struct lifreq));
(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
- isv6 = lif->lif_pif->pif_isv6;
+ isv6 = pif->pif_isv6;
fd = isv6 ? v6_sock_fd : v4_sock_fd;
if (ioctl(fd, SIOCGLIFFLAGS, &lifr) == -1) {
@@ -689,43 +739,41 @@ verify_lif(const dhcp_lif_t *lif)
}
/*
- * Special case: if the interface has gone down as a duplicate, then
- * this alone does _not_ mean that we're abandoning it just yet. Allow
- * the state machine to handle this normally by trying to get a new
- * lease.
- */
- if ((lifr.lifr_flags & (IFF_UP|IFF_DUPLICATE)) == IFF_DUPLICATE) {
- dhcpmsg(MSG_DEBUG, "verify_lif: duplicate address on %s",
- lif->lif_name);
- return (B_TRUE);
- }
-
- /*
- * If the user has torn down or started up the interface manually, then
- * abandon the lease.
- */
- if ((lif->lif_flags ^ lifr.lifr_flags) & IFF_UP) {
- dhcpmsg(MSG_DEBUG, "verify_lif: user has %s %s",
- lifr.lifr_flags & IFF_UP ? "started up" : "shut down",
- lif->lif_name);
- return (B_FALSE);
- }
-
- /*
* Check for delete and recreate.
*/
if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
- dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed on %s",
- lif->lif_name);
+ if (errno != ENXIO) {
+ dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX failed "
+ "on %s", lif->lif_name);
+ }
return (B_FALSE);
}
- if (lifr.lifr_index != lif->lif_pif->pif_index) {
+ if (lifr.lifr_index != pif->pif_index) {
dhcpmsg(MSG_DEBUG,
"verify_lif: ifindex on %s changed: %u to %u",
- lif->lif_name, lif->lif_pif->pif_index, lifr.lifr_index);
+ lif->lif_name, pif->pif_index, lifr.lifr_index);
return (B_FALSE);
}
+ if (pif->pif_under_ipmp) {
+ (void) strlcpy(lifr.lifr_name, pif->pif_grifname, LIFNAMSIZ);
+
+ if (ioctl(fd, SIOCGLIFINDEX, &lifr) == -1) {
+ if (errno != ENXIO) {
+ dhcpmsg(MSG_ERR, "verify_lif: SIOCGLIFINDEX "
+ "failed on %s", lifr.lifr_name);
+ }
+ return (B_FALSE);
+ }
+
+ if (lifr.lifr_index != pif->pif_grindex) {
+ dhcpmsg(MSG_DEBUG, "verify_lif: IPMP group ifindex "
+ "on %s changed: %u to %u", lifr.lifr_name,
+ pif->pif_grindex, lifr.lifr_index);
+ return (B_FALSE);
+ }
+ }
+
/*
* If the IP address, netmask, or broadcast address have changed, or
* the interface has been unplumbed, then we act like there has been an
@@ -934,6 +982,13 @@ plumb_lif(dhcp_pif_t *pif, const in6_addr_t *addr)
lifr.lifr_name);
goto failure;
}
+
+ /*
+ * See comment in set_lif_dhcp().
+ */
+ if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER))
+ lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
+
lifr.lifr_flags |= IFF_UP | IFF_DHCPRUNNING;
if (ioctl(v6_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
dhcpmsg(MSG_ERR, "plumb_lif: SIOCSLIFFLAGS %s",
@@ -1060,8 +1115,9 @@ set_lif_dhcp(dhcp_lif_t *lif, boolean_t is_adopting)
int fd;
int err;
struct lifreq lifr;
+ dhcp_pif_t *pif = lif->lif_pif;
- fd = lif->lif_pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
+ fd = pif->pif_isv6 ? v6_sock_fd : v4_sock_fd;
(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
@@ -1098,6 +1154,17 @@ set_lif_dhcp(dhcp_lif_t *lif, boolean_t is_adopting)
"set on %s", lif->lif_name);
}
} else {
+ /*
+ * If the lif is on an interface under IPMP, IFF_NOFAILOVER
+ * must be set or the kernel will prevent us from setting
+ * IFF_DHCPRUNNING (since the subsequent IFF_UP would lead to
+ * migration). We set IFF_DEPRECATED too since the kernel
+ * will set it automatically when setting IFF_NOFAILOVER,
+ * causing our lif_flags value to grow stale.
+ */
+ if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER))
+ lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
+
lifr.lifr_flags |= IFF_DHCPRUNNING;
if (ioctl(fd, SIOCSLIFFLAGS, &lifr) == -1) {
dhcpmsg(MSG_ERR, "set_lif_dhcp: SIOCSLIFFLAGS for %s",
@@ -1207,6 +1274,13 @@ clear_lif_deprecated(dhcp_lif_t *lif)
return (B_FALSE);
}
+ /*
+ * Don't try to clear IFF_DEPRECATED if this is a test address,
+ * since IPMP's use of IFF_DEPRECATED is not compatible with ours.
+ */
+ if (lifr.lifr_flags & IFF_NOFAILOVER)
+ return (B_TRUE);
+
if (!(lifr.lifr_flags & IFF_DEPRECATED))
return (B_TRUE);
@@ -1226,16 +1300,19 @@ clear_lif_deprecated(dhcp_lif_t *lif)
*
* input: dhcp_lif_t *: the logical interface to operate on
* in_addr_t: the address the socket will be bound to (in hbo)
+ * boolean_t: B_TRUE if the address should be brought up (if needed)
* output: boolean_t: B_TRUE if the socket was opened successfully.
*/
boolean_t
-open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo)
+open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo, boolean_t bringup)
{
const char *errmsg;
struct lifreq lifr;
int on = 1;
uchar_t ttl = 255;
+ uint32_t ifindex;
+ dhcp_pif_t *pif = lif->lif_pif;
if (lif->lif_sock_ip_fd != -1) {
dhcpmsg(MSG_WARNING, "open_ip_lif: socket already open on %s",
@@ -1270,7 +1347,7 @@ open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo)
}
if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_DHCPINIT_IF,
- &lif->lif_pif->pif_index, sizeof (int)) == -1) {
+ &pif->pif_index, sizeof (int)) == -1) {
errmsg = "cannot set IP_DHCPINIT_IF";
goto failure;
}
@@ -1288,23 +1365,40 @@ open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo)
goto failure;
}
- if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF,
- &lif->lif_pif->pif_index, sizeof (int)) == -1) {
+ ifindex = pif->pif_under_ipmp ? pif->pif_grindex : pif->pif_index;
+ if (setsockopt(lif->lif_sock_ip_fd, IPPROTO_IP, IP_BOUND_IF, &ifindex,
+ sizeof (int)) == -1) {
errmsg = "cannot set IP_BOUND_IF";
goto failure;
}
- /*
- * Make sure at least one lif on the interface we used in IP_BOUND_IF
- * is IFF_UP so that we can send and receive IP packets.
- */
(void) strlcpy(lifr.lifr_name, lif->lif_name, LIFNAMSIZ);
if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
errmsg = "cannot get interface flags";
goto failure;
}
- if (!(lifr.lifr_flags & IFF_UP)) {
+ /*
+ * If the lif is part of an interface under IPMP, IFF_NOFAILOVER must
+ * be set or the kernel will prevent us from setting IFF_DHCPRUNNING
+ * (since the subsequent IFF_UP would lead to migration). We set
+ * IFF_DEPRECATED too since the kernel will set it automatically when
+ * setting IFF_NOFAILOVER, causing our lif_flags value to grow stale.
+ */
+ if (pif->pif_under_ipmp && !(lifr.lifr_flags & IFF_NOFAILOVER)) {
+ lifr.lifr_flags |= IFF_NOFAILOVER | IFF_DEPRECATED;
+ if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
+ errmsg = "cannot set IFF_NOFAILOVER";
+ goto failure;
+ }
+ }
+ lif->lif_flags = lifr.lifr_flags;
+
+ /*
+ * If this is initial bringup, make sure the address we're acquiring a
+ * lease on is IFF_UP.
+ */
+ if (bringup && !(lifr.lifr_flags & IFF_UP)) {
/*
* Start from a clean slate.
*/
@@ -1330,6 +1424,30 @@ open_ip_lif(dhcp_lif_t *lif, in_addr_t addr_hbo)
((struct sockaddr_in *)&lifr.lifr_addr)->sin_addr.s_addr;
}
+ /*
+ * Usually, bringing up the address we're acquiring a lease on is
+ * sufficient to allow packets to be sent and received via the
+ * IP_BOUND_IF we did earlier. However, if we're acquiring a lease on
+ * an underlying IPMP interface, the group interface will be used for
+ * sending and receiving IP packets via IP_BOUND_IF. Thus, ensure at
+ * least one address on the group interface is IFF_UP.
+ */
+ if (bringup && pif->pif_under_ipmp) {
+ (void) strlcpy(lifr.lifr_name, pif->pif_grifname, LIFNAMSIZ);
+ if (ioctl(v4_sock_fd, SIOCGLIFFLAGS, &lifr) == -1) {
+ errmsg = "cannot get IPMP group interface flags";
+ goto failure;
+ }
+
+ if (!(lifr.lifr_flags & IFF_UP)) {
+ lifr.lifr_flags |= IFF_UP;
+ if (ioctl(v4_sock_fd, SIOCSLIFFLAGS, &lifr) == -1) {
+ errmsg = "cannot bring up IPMP group interface";
+ goto failure;
+ }
+ }
+ }
+
lif->lif_packet_id = iu_register_event(eh, lif->lif_sock_ip_fd, POLLIN,
dhcp_packet_lif, lif);
if (lif->lif_packet_id == -1) {