summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdhcpagent/common/dhcp_stable.c
diff options
context:
space:
mode:
authorcarlsonj <none@none>2007-01-17 08:41:37 -0800
committercarlsonj <none@none>2007-01-17 08:41:37 -0800
commitd04ccbb3f3163ae5962a8b7465d9796bff6ca434 (patch)
treeee9becc340fed8326cfa6bac8d30f7d4b18313ce /usr/src/lib/libdhcpagent/common/dhcp_stable.c
parent1c25cdbd0f6ba3ec11a8ab1653c801027a1ffc61 (diff)
downloadillumos-gate-d04ccbb3f3163ae5962a8b7465d9796bff6ca434.tar.gz
PSARC 2006/597 DHCPv6 Client
4863327 dhcp client causes drag 6369116 dhcpagent doesn't notice when logical interfaces disappear 6386331 dhcpagent should implement RFC 4361 client identifier 6450744 dhcpagent prevents aggrs from being deleted 6462355 impact of RELEASE_ON_SIGTERM should be clearer 6464136 Solaris should support the client side of DHCPv6 6482163 libxnet lint library lacks __EXTENSIONS__ 6485164 dead rule_zone_specific() rule in ipif_select_source_v6() 6487534 snoop "dhcp" filtering keyword doesn't actually work 6487958 async internal timeout can trample on renew/rebind timer
Diffstat (limited to 'usr/src/lib/libdhcpagent/common/dhcp_stable.c')
-rw-r--r--usr/src/lib/libdhcpagent/common/dhcp_stable.c308
1 files changed, 308 insertions, 0 deletions
diff --git a/usr/src/lib/libdhcpagent/common/dhcp_stable.c b/usr/src/lib/libdhcpagent/common/dhcp_stable.c
new file mode 100644
index 0000000000..7ae11346f6
--- /dev/null
+++ b/usr/src/lib/libdhcpagent/common/dhcp_stable.c
@@ -0,0 +1,308 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This module reads and writes the stable identifier values, DUID and IAID.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <libdlpi.h>
+#include <uuid/uuid.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <net/if.h>
+#include <netinet/dhcp6.h>
+#include <dhcp_inittab.h>
+
+#define DUID_FILE "/etc/dhcp/duid"
+#define IAID_FILE "/etc/dhcp/iaid"
+
+struct iaid_ent {
+ uint32_t ie_iaid;
+ char ie_name[LIFNAMSIZ];
+};
+
+/*
+ * read_stable_duid(): read the system's stable DUID, if any
+ *
+ * input: size_t *: pointer to a size_t to return the DUID length
+ * output: uchar_t *: the DUID buffer, or NULL on error (and errno is set)
+ * note: memory returned is from malloc; caller must free.
+ */
+
+uchar_t *
+read_stable_duid(size_t *duidlen)
+{
+ int fd;
+ ssize_t retv;
+ struct stat sb;
+ uchar_t *duid = NULL;
+
+ if ((fd = open(DUID_FILE, O_RDONLY)) == -1)
+ return (NULL);
+ if (fstat(fd, &sb) != -1 && S_ISREG(sb.st_mode) &&
+ (duid = malloc(sb.st_size)) != NULL) {
+ retv = read(fd, duid, sb.st_size);
+ if (retv == sb.st_size) {
+ *duidlen = sb.st_size;
+ } else {
+ free(duid);
+ /*
+ * Make sure that errno always gets set when something
+ * goes wrong.
+ */
+ if (retv >= 0)
+ errno = EINVAL;
+ duid = NULL;
+ }
+ }
+ (void) close(fd);
+ return (duid);
+}
+
+/*
+ * write_stable_duid(): write the system's stable DUID.
+ *
+ * input: const uchar_t *: pointer to the DUID buffer
+ * size_t: length of the DUID
+ * output: int: 0 on success, -1 on error. errno is set on error.
+ */
+
+int
+write_stable_duid(const uchar_t *duid, size_t duidlen)
+{
+ int fd;
+ ssize_t retv;
+
+ (void) unlink(DUID_FILE);
+ if ((fd = open(DUID_FILE, O_WRONLY | O_CREAT, 0644)) == -1)
+ return (-1);
+ retv = write(fd, duid, duidlen);
+ if (retv == duidlen) {
+ return (close(fd));
+ } else {
+ (void) close(fd);
+ if (retv >= 0)
+ errno = ENOSPC;
+ return (-1);
+ }
+}
+
+/*
+ * make_stable_duid(): create a new DUID
+ *
+ * input: const char *: name of physical interface for reference
+ * size_t *: pointer to a size_t to return the DUID length
+ * output: uchar_t *: the DUID buffer, or NULL on error (and errno is set)
+ * note: memory returned is from malloc; caller must free.
+ */
+
+uchar_t *
+make_stable_duid(const char *physintf, size_t *duidlen)
+{
+ int fd, len;
+ dl_info_ack_t dl_info;
+ dlpi_if_attr_t dia;
+ duid_en_t *den;
+
+ /*
+ * Try to read the MAC layer address for the physical interface
+ * provided as a hint. If that works, we can use a DUID-LLT.
+ */
+
+ fd = dlpi_if_open(physintf, &dia, B_FALSE);
+ if (fd != -1 &&
+ dlpi_info(fd, -1, &dl_info, NULL, NULL, NULL, NULL, NULL,
+ NULL) != -1 &&
+ (len = dl_info.dl_addr_length - abs(dl_info.dl_sap_length)) > 0) {
+ duid_llt_t *dllt;
+ uint_t arptype;
+
+ arptype = dlpi_to_arp(dl_info.dl_mac_type);
+
+ if ((dllt = malloc(sizeof (*dllt) + len)) == NULL) {
+ (void) dlpi_close(fd);
+ return (NULL);
+ }
+ if (arptype != 0 && dlpi_phys_addr(fd, -1, DL_CURR_PHYS_ADDR,
+ (uint8_t *)(dllt + 1), NULL) == 0) {
+ time_t now;
+
+ dllt->dllt_dutype = htons(DHCPV6_DUID_LLT);
+ dllt->dllt_hwtype = htons(arptype);
+ now = time(NULL) - DUID_TIME_BASE;
+ dllt->dllt_time = htonl(now);
+ *duidlen = sizeof (*dllt) + len;
+ return ((uchar_t *)dllt);
+ }
+ free(dllt);
+ }
+ if (fd != -1)
+ (void) dlpi_close(fd);
+
+ /*
+ * If we weren't able to create a DUID based on the network interface
+ * in use, then generate one based on a UUID.
+ */
+ den = malloc(sizeof (*den) + UUID_LEN);
+ if (den != NULL) {
+ uuid_t uuid;
+
+ den->den_dutype = htons(DHCPV6_DUID_EN);
+ DHCPV6_SET_ENTNUM(den, DHCPV6_SUN_ENT);
+ uuid_generate(uuid);
+ (void) memcpy(den + 1, uuid, UUID_LEN);
+ *duidlen = sizeof (*den) + UUID_LEN;
+ }
+ return ((uchar_t *)den);
+}
+
+/*
+ * read_stable_iaid(): read a link's stable IAID, if any
+ *
+ * input: const char *: interface name
+ * output: uint32_t: the IAID, or 0 if none
+ */
+
+uint32_t
+read_stable_iaid(const char *intf)
+{
+ int fd;
+ struct iaid_ent ie;
+
+ if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
+ return (0);
+ while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
+ if (strcmp(intf, ie.ie_name) == 0) {
+ (void) close(fd);
+ return (ie.ie_iaid);
+ }
+ }
+ (void) close(fd);
+ return (0);
+}
+
+/*
+ * write_stable_iaid(): write out a link's stable IAID
+ *
+ * input: const char *: interface name
+ * output: uint32_t: the IAID, or 0 if none
+ */
+
+int
+write_stable_iaid(const char *intf, uint32_t iaid)
+{
+ int fd;
+ struct iaid_ent ie;
+ ssize_t retv;
+
+ if ((fd = open(IAID_FILE, O_RDWR | O_CREAT, 0644)) == -1)
+ return (0);
+ while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
+ if (strcmp(intf, ie.ie_name) == 0) {
+ (void) close(fd);
+ if (iaid == ie.ie_iaid) {
+ return (0);
+ } else {
+ errno = EINVAL;
+ return (-1);
+ }
+ }
+ }
+ (void) memset(&ie, 0, sizeof (ie));
+ ie.ie_iaid = iaid;
+ (void) strlcpy(ie.ie_name, intf, sizeof (ie.ie_name));
+ retv = write(fd, &ie, sizeof (ie));
+ (void) close(fd);
+ if (retv == sizeof (ie)) {
+ return (0);
+ } else {
+ if (retv >= 0)
+ errno = ENOSPC;
+ return (-1);
+ }
+}
+
+/*
+ * make_stable_iaid(): create a stable IAID for a link
+ *
+ * input: const char *: interface name
+ * uint32_t: the ifIndex for this link (as a "hint")
+ * output: uint32_t: the new IAID, never zero
+ */
+
+/* ARGSUSED */
+uint32_t
+make_stable_iaid(const char *intf, uint32_t hint)
+{
+ int fd;
+ struct iaid_ent ie;
+ uint32_t maxid, minunused;
+ boolean_t recheck;
+
+ if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
+ return (hint);
+ maxid = 0;
+ minunused = 1;
+ /*
+ * This logic is deliberately unoptimized. The reason is that it runs
+ * essentially just once per interface for the life of the system.
+ * Once the IAID is established, there's no reason to generate it
+ * again, and all we care about here is correctness. Also, IAIDs tend
+ * to get added in a logical sequence order, so the outer loop should
+ * not normally run more than twice.
+ */
+ do {
+ recheck = B_FALSE;
+ while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
+ if (ie.ie_iaid > maxid)
+ maxid = ie.ie_iaid;
+ if (ie.ie_iaid == minunused) {
+ recheck = B_TRUE;
+ minunused++;
+ }
+ if (ie.ie_iaid == hint)
+ hint = 0;
+ }
+ if (recheck)
+ (void) lseek(fd, 0, SEEK_SET);
+ } while (recheck);
+ (void) close(fd);
+ if (hint != 0)
+ return (hint);
+ else if (maxid != UINT32_MAX)
+ return (maxid + 1);
+ else
+ return (minunused);
+}