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