summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdhcpagent/common/dhcp_stable.c
diff options
context:
space:
mode:
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);
+}