diff options
Diffstat (limited to 'usr/src/lib/libdhcpagent/common/dhcp_stable.c')
| -rw-r--r-- | usr/src/lib/libdhcpagent/common/dhcp_stable.c | 308 |
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); +} |
