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