/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #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 len; dlpi_info_t dlinfo; dlpi_handle_t dh = NULL; uint_t arptype; 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. */ if (dlpi_open(physintf, &dh, 0) == DLPI_SUCCESS && dlpi_info(dh, &dlinfo, 0) == DLPI_SUCCESS && (len = dlinfo.di_physaddrlen) > 0 && (arptype = dlpi_to_arp(dlinfo.di_mactype) != 0)) { duid_llt_t *dllt; time_t now; if ((dllt = malloc(sizeof (*dllt) + len)) == NULL) { dlpi_close(dh); return (NULL); } (void) memcpy((dllt + 1), dlinfo.di_physaddr, len); 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; dlpi_close(dh); return ((uchar_t *)dllt); } if (dh != NULL) dlpi_close(dh); /* * 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); }