xref: /illumos-gate/usr/src/lib/libdhcpagent/common/dhcp_stable.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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