1d04ccbb3Scarlsonj /* 2d04ccbb3Scarlsonj * CDDL HEADER START 3d04ccbb3Scarlsonj * 4d04ccbb3Scarlsonj * The contents of this file are subject to the terms of the 5d04ccbb3Scarlsonj * Common Development and Distribution License (the "License"). 6d04ccbb3Scarlsonj * You may not use this file except in compliance with the License. 7d04ccbb3Scarlsonj * 8d04ccbb3Scarlsonj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9d04ccbb3Scarlsonj * or http://www.opensolaris.org/os/licensing. 10d04ccbb3Scarlsonj * See the License for the specific language governing permissions 11d04ccbb3Scarlsonj * and limitations under the License. 12d04ccbb3Scarlsonj * 13d04ccbb3Scarlsonj * When distributing Covered Code, include this CDDL HEADER in each 14d04ccbb3Scarlsonj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15d04ccbb3Scarlsonj * If applicable, add the following below this CDDL HEADER, with the 16d04ccbb3Scarlsonj * fields enclosed by brackets "[]" replaced with your own identifying 17d04ccbb3Scarlsonj * information: Portions Copyright [yyyy] [name of copyright owner] 18d04ccbb3Scarlsonj * 19d04ccbb3Scarlsonj * CDDL HEADER END 20d04ccbb3Scarlsonj */ 21d04ccbb3Scarlsonj 22d04ccbb3Scarlsonj /* 23d04ccbb3Scarlsonj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24d04ccbb3Scarlsonj * Use is subject to license terms. 25d04ccbb3Scarlsonj */ 26d04ccbb3Scarlsonj 27d04ccbb3Scarlsonj /* 28d04ccbb3Scarlsonj * This module reads and writes the stable identifier values, DUID and IAID. 29d04ccbb3Scarlsonj */ 30d04ccbb3Scarlsonj 31d04ccbb3Scarlsonj #include <stdio.h> 32d04ccbb3Scarlsonj #include <stdlib.h> 33d04ccbb3Scarlsonj #include <unistd.h> 34d04ccbb3Scarlsonj #include <string.h> 35d04ccbb3Scarlsonj #include <limits.h> 36d04ccbb3Scarlsonj #include <fcntl.h> 37d04ccbb3Scarlsonj #include <errno.h> 38d04ccbb3Scarlsonj #include <libdlpi.h> 39d04ccbb3Scarlsonj #include <uuid/uuid.h> 40d04ccbb3Scarlsonj #include <sys/types.h> 41d04ccbb3Scarlsonj #include <sys/stat.h> 42d04ccbb3Scarlsonj #include <net/if.h> 43d04ccbb3Scarlsonj #include <netinet/dhcp6.h> 44d04ccbb3Scarlsonj #include <dhcp_inittab.h> 45d04ccbb3Scarlsonj 46d04ccbb3Scarlsonj #define DUID_FILE "/etc/dhcp/duid" 47d04ccbb3Scarlsonj #define IAID_FILE "/etc/dhcp/iaid" 48d04ccbb3Scarlsonj 49d04ccbb3Scarlsonj struct iaid_ent { 50d04ccbb3Scarlsonj uint32_t ie_iaid; 51d04ccbb3Scarlsonj char ie_name[LIFNAMSIZ]; 52d04ccbb3Scarlsonj }; 53d04ccbb3Scarlsonj 54d04ccbb3Scarlsonj /* 55d04ccbb3Scarlsonj * read_stable_duid(): read the system's stable DUID, if any 56d04ccbb3Scarlsonj * 57d04ccbb3Scarlsonj * input: size_t *: pointer to a size_t to return the DUID length 58d04ccbb3Scarlsonj * output: uchar_t *: the DUID buffer, or NULL on error (and errno is set) 59d04ccbb3Scarlsonj * note: memory returned is from malloc; caller must free. 60d04ccbb3Scarlsonj */ 61d04ccbb3Scarlsonj 62d04ccbb3Scarlsonj uchar_t * 63d04ccbb3Scarlsonj read_stable_duid(size_t *duidlen) 64d04ccbb3Scarlsonj { 65d04ccbb3Scarlsonj int fd; 66d04ccbb3Scarlsonj ssize_t retv; 67d04ccbb3Scarlsonj struct stat sb; 68d04ccbb3Scarlsonj uchar_t *duid = NULL; 69d04ccbb3Scarlsonj 70d04ccbb3Scarlsonj if ((fd = open(DUID_FILE, O_RDONLY)) == -1) 71d04ccbb3Scarlsonj return (NULL); 72d04ccbb3Scarlsonj if (fstat(fd, &sb) != -1 && S_ISREG(sb.st_mode) && 73d04ccbb3Scarlsonj (duid = malloc(sb.st_size)) != NULL) { 74d04ccbb3Scarlsonj retv = read(fd, duid, sb.st_size); 75d04ccbb3Scarlsonj if (retv == sb.st_size) { 76d04ccbb3Scarlsonj *duidlen = sb.st_size; 77d04ccbb3Scarlsonj } else { 78d04ccbb3Scarlsonj free(duid); 79d04ccbb3Scarlsonj /* 80d04ccbb3Scarlsonj * Make sure that errno always gets set when something 81d04ccbb3Scarlsonj * goes wrong. 82d04ccbb3Scarlsonj */ 83d04ccbb3Scarlsonj if (retv >= 0) 84d04ccbb3Scarlsonj errno = EINVAL; 85d04ccbb3Scarlsonj duid = NULL; 86d04ccbb3Scarlsonj } 87d04ccbb3Scarlsonj } 88d04ccbb3Scarlsonj (void) close(fd); 89d04ccbb3Scarlsonj return (duid); 90d04ccbb3Scarlsonj } 91d04ccbb3Scarlsonj 92d04ccbb3Scarlsonj /* 93d04ccbb3Scarlsonj * write_stable_duid(): write the system's stable DUID. 94d04ccbb3Scarlsonj * 95d04ccbb3Scarlsonj * input: const uchar_t *: pointer to the DUID buffer 96d04ccbb3Scarlsonj * size_t: length of the DUID 97d04ccbb3Scarlsonj * output: int: 0 on success, -1 on error. errno is set on error. 98d04ccbb3Scarlsonj */ 99d04ccbb3Scarlsonj 100d04ccbb3Scarlsonj int 101d04ccbb3Scarlsonj write_stable_duid(const uchar_t *duid, size_t duidlen) 102d04ccbb3Scarlsonj { 103d04ccbb3Scarlsonj int fd; 104d04ccbb3Scarlsonj ssize_t retv; 105d04ccbb3Scarlsonj 106d04ccbb3Scarlsonj (void) unlink(DUID_FILE); 107d04ccbb3Scarlsonj if ((fd = open(DUID_FILE, O_WRONLY | O_CREAT, 0644)) == -1) 108d04ccbb3Scarlsonj return (-1); 109d04ccbb3Scarlsonj retv = write(fd, duid, duidlen); 110d04ccbb3Scarlsonj if (retv == duidlen) { 111d04ccbb3Scarlsonj return (close(fd)); 112d04ccbb3Scarlsonj } else { 113d04ccbb3Scarlsonj (void) close(fd); 114d04ccbb3Scarlsonj if (retv >= 0) 115d04ccbb3Scarlsonj errno = ENOSPC; 116d04ccbb3Scarlsonj return (-1); 117d04ccbb3Scarlsonj } 118d04ccbb3Scarlsonj } 119d04ccbb3Scarlsonj 120d04ccbb3Scarlsonj /* 121d04ccbb3Scarlsonj * make_stable_duid(): create a new DUID 122d04ccbb3Scarlsonj * 123d04ccbb3Scarlsonj * input: const char *: name of physical interface for reference 124d04ccbb3Scarlsonj * size_t *: pointer to a size_t to return the DUID length 125d04ccbb3Scarlsonj * output: uchar_t *: the DUID buffer, or NULL on error (and errno is set) 126d04ccbb3Scarlsonj * note: memory returned is from malloc; caller must free. 127d04ccbb3Scarlsonj */ 128d04ccbb3Scarlsonj 129d04ccbb3Scarlsonj uchar_t * 130d04ccbb3Scarlsonj make_stable_duid(const char *physintf, size_t *duidlen) 131d04ccbb3Scarlsonj { 132c7e4935fSss150715 int len; 133c7e4935fSss150715 dlpi_info_t dlinfo; 134c7e4935fSss150715 dlpi_handle_t dh = NULL; 135c7e4935fSss150715 uint_t arptype; 136d04ccbb3Scarlsonj duid_en_t *den; 137d04ccbb3Scarlsonj 138d04ccbb3Scarlsonj /* 139d04ccbb3Scarlsonj * Try to read the MAC layer address for the physical interface 140d04ccbb3Scarlsonj * provided as a hint. If that works, we can use a DUID-LLT. 141d04ccbb3Scarlsonj */ 142d04ccbb3Scarlsonj 143c7e4935fSss150715 if (dlpi_open(physintf, &dh, 0) == DLPI_SUCCESS && 144*35b6f047SDavid Höppner dlpi_bind(dh, DLPI_ANY_SAP, NULL) == DLPI_SUCCESS && 145c7e4935fSss150715 dlpi_info(dh, &dlinfo, 0) == DLPI_SUCCESS && 146c7e4935fSss150715 (len = dlinfo.di_physaddrlen) > 0 && 147948f2876Sss150715 (arptype = dlpi_arptype(dlinfo.di_mactype) != 0)) { 148d04ccbb3Scarlsonj duid_llt_t *dllt; 149d04ccbb3Scarlsonj time_t now; 150d04ccbb3Scarlsonj 151c7e4935fSss150715 if ((dllt = malloc(sizeof (*dllt) + len)) == NULL) { 152c7e4935fSss150715 dlpi_close(dh); 153c7e4935fSss150715 return (NULL); 154c7e4935fSss150715 } 155c7e4935fSss150715 156c7e4935fSss150715 (void) memcpy((dllt + 1), dlinfo.di_physaddr, len); 157d04ccbb3Scarlsonj dllt->dllt_dutype = htons(DHCPV6_DUID_LLT); 158d04ccbb3Scarlsonj dllt->dllt_hwtype = htons(arptype); 159d04ccbb3Scarlsonj now = time(NULL) - DUID_TIME_BASE; 160d04ccbb3Scarlsonj dllt->dllt_time = htonl(now); 161d04ccbb3Scarlsonj *duidlen = sizeof (*dllt) + len; 162c7e4935fSss150715 dlpi_close(dh); 163d04ccbb3Scarlsonj return ((uchar_t *)dllt); 164d04ccbb3Scarlsonj } 165c7e4935fSss150715 if (dh != NULL) 166c7e4935fSss150715 dlpi_close(dh); 167d04ccbb3Scarlsonj 168d04ccbb3Scarlsonj /* 169d04ccbb3Scarlsonj * If we weren't able to create a DUID based on the network interface 170d04ccbb3Scarlsonj * in use, then generate one based on a UUID. 171d04ccbb3Scarlsonj */ 172d04ccbb3Scarlsonj den = malloc(sizeof (*den) + UUID_LEN); 173d04ccbb3Scarlsonj if (den != NULL) { 174d04ccbb3Scarlsonj uuid_t uuid; 175d04ccbb3Scarlsonj 176d04ccbb3Scarlsonj den->den_dutype = htons(DHCPV6_DUID_EN); 177d04ccbb3Scarlsonj DHCPV6_SET_ENTNUM(den, DHCPV6_SUN_ENT); 178d04ccbb3Scarlsonj uuid_generate(uuid); 179d04ccbb3Scarlsonj (void) memcpy(den + 1, uuid, UUID_LEN); 180d04ccbb3Scarlsonj *duidlen = sizeof (*den) + UUID_LEN; 181d04ccbb3Scarlsonj } 182d04ccbb3Scarlsonj return ((uchar_t *)den); 183d04ccbb3Scarlsonj } 184d04ccbb3Scarlsonj 185d04ccbb3Scarlsonj /* 186d04ccbb3Scarlsonj * read_stable_iaid(): read a link's stable IAID, if any 187d04ccbb3Scarlsonj * 188d04ccbb3Scarlsonj * input: const char *: interface name 189d04ccbb3Scarlsonj * output: uint32_t: the IAID, or 0 if none 190d04ccbb3Scarlsonj */ 191d04ccbb3Scarlsonj 192d04ccbb3Scarlsonj uint32_t 193d04ccbb3Scarlsonj read_stable_iaid(const char *intf) 194d04ccbb3Scarlsonj { 195d04ccbb3Scarlsonj int fd; 196d04ccbb3Scarlsonj struct iaid_ent ie; 197d04ccbb3Scarlsonj 198d04ccbb3Scarlsonj if ((fd = open(IAID_FILE, O_RDONLY)) == -1) 199d04ccbb3Scarlsonj return (0); 200d04ccbb3Scarlsonj while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) { 201d04ccbb3Scarlsonj if (strcmp(intf, ie.ie_name) == 0) { 202d04ccbb3Scarlsonj (void) close(fd); 203d04ccbb3Scarlsonj return (ie.ie_iaid); 204d04ccbb3Scarlsonj } 205d04ccbb3Scarlsonj } 206d04ccbb3Scarlsonj (void) close(fd); 207d04ccbb3Scarlsonj return (0); 208d04ccbb3Scarlsonj } 209d04ccbb3Scarlsonj 210d04ccbb3Scarlsonj /* 211d04ccbb3Scarlsonj * write_stable_iaid(): write out a link's stable IAID 212d04ccbb3Scarlsonj * 213d04ccbb3Scarlsonj * input: const char *: interface name 214d04ccbb3Scarlsonj * output: uint32_t: the IAID, or 0 if none 215d04ccbb3Scarlsonj */ 216d04ccbb3Scarlsonj 217d04ccbb3Scarlsonj int 218d04ccbb3Scarlsonj write_stable_iaid(const char *intf, uint32_t iaid) 219d04ccbb3Scarlsonj { 220d04ccbb3Scarlsonj int fd; 221d04ccbb3Scarlsonj struct iaid_ent ie; 222d04ccbb3Scarlsonj ssize_t retv; 223d04ccbb3Scarlsonj 224d04ccbb3Scarlsonj if ((fd = open(IAID_FILE, O_RDWR | O_CREAT, 0644)) == -1) 225d04ccbb3Scarlsonj return (0); 226d04ccbb3Scarlsonj while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) { 227d04ccbb3Scarlsonj if (strcmp(intf, ie.ie_name) == 0) { 228d04ccbb3Scarlsonj (void) close(fd); 229d04ccbb3Scarlsonj if (iaid == ie.ie_iaid) { 230d04ccbb3Scarlsonj return (0); 231d04ccbb3Scarlsonj } else { 232d04ccbb3Scarlsonj errno = EINVAL; 233d04ccbb3Scarlsonj return (-1); 234d04ccbb3Scarlsonj } 235d04ccbb3Scarlsonj } 236d04ccbb3Scarlsonj } 237d04ccbb3Scarlsonj (void) memset(&ie, 0, sizeof (ie)); 238d04ccbb3Scarlsonj ie.ie_iaid = iaid; 239d04ccbb3Scarlsonj (void) strlcpy(ie.ie_name, intf, sizeof (ie.ie_name)); 240d04ccbb3Scarlsonj retv = write(fd, &ie, sizeof (ie)); 241d04ccbb3Scarlsonj (void) close(fd); 242d04ccbb3Scarlsonj if (retv == sizeof (ie)) { 243d04ccbb3Scarlsonj return (0); 244d04ccbb3Scarlsonj } else { 245d04ccbb3Scarlsonj if (retv >= 0) 246d04ccbb3Scarlsonj errno = ENOSPC; 247d04ccbb3Scarlsonj return (-1); 248d04ccbb3Scarlsonj } 249d04ccbb3Scarlsonj } 250d04ccbb3Scarlsonj 251d04ccbb3Scarlsonj /* 252d04ccbb3Scarlsonj * make_stable_iaid(): create a stable IAID for a link 253d04ccbb3Scarlsonj * 254d04ccbb3Scarlsonj * input: const char *: interface name 255d04ccbb3Scarlsonj * uint32_t: the ifIndex for this link (as a "hint") 256d04ccbb3Scarlsonj * output: uint32_t: the new IAID, never zero 257d04ccbb3Scarlsonj */ 258d04ccbb3Scarlsonj 259d04ccbb3Scarlsonj /* ARGSUSED */ 260d04ccbb3Scarlsonj uint32_t 261d04ccbb3Scarlsonj make_stable_iaid(const char *intf, uint32_t hint) 262d04ccbb3Scarlsonj { 263d04ccbb3Scarlsonj int fd; 264d04ccbb3Scarlsonj struct iaid_ent ie; 265d04ccbb3Scarlsonj uint32_t maxid, minunused; 266d04ccbb3Scarlsonj boolean_t recheck; 267d04ccbb3Scarlsonj 268d04ccbb3Scarlsonj if ((fd = open(IAID_FILE, O_RDONLY)) == -1) 269d04ccbb3Scarlsonj return (hint); 270d04ccbb3Scarlsonj maxid = 0; 271d04ccbb3Scarlsonj minunused = 1; 272d04ccbb3Scarlsonj /* 273d04ccbb3Scarlsonj * This logic is deliberately unoptimized. The reason is that it runs 274d04ccbb3Scarlsonj * essentially just once per interface for the life of the system. 275d04ccbb3Scarlsonj * Once the IAID is established, there's no reason to generate it 276d04ccbb3Scarlsonj * again, and all we care about here is correctness. Also, IAIDs tend 277d04ccbb3Scarlsonj * to get added in a logical sequence order, so the outer loop should 278d04ccbb3Scarlsonj * not normally run more than twice. 279d04ccbb3Scarlsonj */ 280d04ccbb3Scarlsonj do { 281d04ccbb3Scarlsonj recheck = B_FALSE; 282d04ccbb3Scarlsonj while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) { 283d04ccbb3Scarlsonj if (ie.ie_iaid > maxid) 284d04ccbb3Scarlsonj maxid = ie.ie_iaid; 285d04ccbb3Scarlsonj if (ie.ie_iaid == minunused) { 286d04ccbb3Scarlsonj recheck = B_TRUE; 287d04ccbb3Scarlsonj minunused++; 288d04ccbb3Scarlsonj } 289d04ccbb3Scarlsonj if (ie.ie_iaid == hint) 290d04ccbb3Scarlsonj hint = 0; 291d04ccbb3Scarlsonj } 292d04ccbb3Scarlsonj if (recheck) 293d04ccbb3Scarlsonj (void) lseek(fd, 0, SEEK_SET); 294d04ccbb3Scarlsonj } while (recheck); 295d04ccbb3Scarlsonj (void) close(fd); 296d04ccbb3Scarlsonj if (hint != 0) 297d04ccbb3Scarlsonj return (hint); 298d04ccbb3Scarlsonj else if (maxid != UINT32_MAX) 299d04ccbb3Scarlsonj return (maxid + 1); 300d04ccbb3Scarlsonj else 301d04ccbb3Scarlsonj return (minunused); 302d04ccbb3Scarlsonj } 303