xref: /illumos-gate/usr/src/lib/libdhcpagent/common/dhcp_stable.c (revision 058561cbaa119a6f2659bc27ef343e1b47266bb2)
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 	fd, len;
135 	dl_info_ack_t dl_info;
136 	dlpi_if_attr_t	dia;
137 	duid_en_t *den;
138 
139 	/*
140 	 * Try to read the MAC layer address for the physical interface
141 	 * provided as a hint.  If that works, we can use a DUID-LLT.
142 	 */
143 
144 	fd = dlpi_if_open(physintf, &dia, B_FALSE);
145 	if (fd != -1 &&
146 	    dlpi_info(fd, -1, &dl_info, NULL, NULL, NULL, NULL, NULL,
147 	    NULL) != -1 &&
148 	    (len = dl_info.dl_addr_length - abs(dl_info.dl_sap_length)) > 0) {
149 		duid_llt_t *dllt;
150 		uint_t arptype;
151 
152 		arptype = dlpi_to_arp(dl_info.dl_mac_type);
153 
154 		if ((dllt = malloc(sizeof (*dllt) + len)) == NULL) {
155 			(void) dlpi_close(fd);
156 			return (NULL);
157 		}
158 		if (arptype != 0 && dlpi_phys_addr(fd, -1, DL_CURR_PHYS_ADDR,
159 		    (uint8_t *)(dllt + 1), NULL) == 0) {
160 			time_t now;
161 
162 			dllt->dllt_dutype = htons(DHCPV6_DUID_LLT);
163 			dllt->dllt_hwtype = htons(arptype);
164 			now = time(NULL) - DUID_TIME_BASE;
165 			dllt->dllt_time = htonl(now);
166 			*duidlen = sizeof (*dllt) + len;
167 			return ((uchar_t *)dllt);
168 		}
169 		free(dllt);
170 	}
171 	if (fd != -1)
172 		(void) dlpi_close(fd);
173 
174 	/*
175 	 * If we weren't able to create a DUID based on the network interface
176 	 * in use, then generate one based on a UUID.
177 	 */
178 	den = malloc(sizeof (*den) + UUID_LEN);
179 	if (den != NULL) {
180 		uuid_t uuid;
181 
182 		den->den_dutype = htons(DHCPV6_DUID_EN);
183 		DHCPV6_SET_ENTNUM(den, DHCPV6_SUN_ENT);
184 		uuid_generate(uuid);
185 		(void) memcpy(den + 1, uuid, UUID_LEN);
186 		*duidlen = sizeof (*den) + UUID_LEN;
187 	}
188 	return ((uchar_t *)den);
189 }
190 
191 /*
192  * read_stable_iaid(): read a link's stable IAID, if any
193  *
194  *   input: const char *: interface name
195  *  output: uint32_t: the IAID, or 0 if none
196  */
197 
198 uint32_t
199 read_stable_iaid(const char *intf)
200 {
201 	int fd;
202 	struct iaid_ent ie;
203 
204 	if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
205 		return (0);
206 	while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
207 		if (strcmp(intf, ie.ie_name) == 0) {
208 			(void) close(fd);
209 			return (ie.ie_iaid);
210 		}
211 	}
212 	(void) close(fd);
213 	return (0);
214 }
215 
216 /*
217  * write_stable_iaid(): write out a link's stable IAID
218  *
219  *   input: const char *: interface name
220  *  output: uint32_t: the IAID, or 0 if none
221  */
222 
223 int
224 write_stable_iaid(const char *intf, uint32_t iaid)
225 {
226 	int fd;
227 	struct iaid_ent ie;
228 	ssize_t retv;
229 
230 	if ((fd = open(IAID_FILE, O_RDWR | O_CREAT, 0644)) == -1)
231 		return (0);
232 	while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
233 		if (strcmp(intf, ie.ie_name) == 0) {
234 			(void) close(fd);
235 			if (iaid == ie.ie_iaid) {
236 				return (0);
237 			} else {
238 				errno = EINVAL;
239 				return (-1);
240 			}
241 		}
242 	}
243 	(void) memset(&ie, 0, sizeof (ie));
244 	ie.ie_iaid = iaid;
245 	(void) strlcpy(ie.ie_name, intf, sizeof (ie.ie_name));
246 	retv = write(fd, &ie, sizeof (ie));
247 	(void) close(fd);
248 	if (retv == sizeof (ie)) {
249 		return (0);
250 	} else {
251 		if (retv >= 0)
252 			errno = ENOSPC;
253 		return (-1);
254 	}
255 }
256 
257 /*
258  * make_stable_iaid(): create a stable IAID for a link
259  *
260  *   input: const char *: interface name
261  *	    uint32_t: the ifIndex for this link (as a "hint")
262  *  output: uint32_t: the new IAID, never zero
263  */
264 
265 /* ARGSUSED */
266 uint32_t
267 make_stable_iaid(const char *intf, uint32_t hint)
268 {
269 	int fd;
270 	struct iaid_ent ie;
271 	uint32_t maxid, minunused;
272 	boolean_t recheck;
273 
274 	if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
275 		return (hint);
276 	maxid = 0;
277 	minunused = 1;
278 	/*
279 	 * This logic is deliberately unoptimized.  The reason is that it runs
280 	 * essentially just once per interface for the life of the system.
281 	 * Once the IAID is established, there's no reason to generate it
282 	 * again, and all we care about here is correctness.  Also, IAIDs tend
283 	 * to get added in a logical sequence order, so the outer loop should
284 	 * not normally run more than twice.
285 	 */
286 	do {
287 		recheck = B_FALSE;
288 		while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
289 			if (ie.ie_iaid > maxid)
290 				maxid = ie.ie_iaid;
291 			if (ie.ie_iaid == minunused) {
292 				recheck = B_TRUE;
293 				minunused++;
294 			}
295 			if (ie.ie_iaid == hint)
296 				hint = 0;
297 		}
298 		if (recheck)
299 			(void) lseek(fd, 0, SEEK_SET);
300 	} while (recheck);
301 	(void) close(fd);
302 	if (hint != 0)
303 		return (hint);
304 	else if (maxid != UINT32_MAX)
305 		return (maxid + 1);
306 	else
307 		return (minunused);
308 }
309