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