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 *
read_stable_duid(size_t * duidlen)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
write_stable_duid(const uchar_t * duid,size_t duidlen)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 *
make_stable_duid(const char * physintf,size_t * duidlen)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
read_stable_iaid(const char * intf)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
write_stable_iaid(const char * intf,uint32_t iaid)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
make_stable_iaid(const char * intf,uint32_t hint)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