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