1d04ccbb3Scarlsonj /*
2d04ccbb3Scarlsonj  * CDDL HEADER START
3d04ccbb3Scarlsonj  *
4d04ccbb3Scarlsonj  * The contents of this file are subject to the terms of the
5d04ccbb3Scarlsonj  * Common Development and Distribution License (the "License").
6d04ccbb3Scarlsonj  * You may not use this file except in compliance with the License.
7d04ccbb3Scarlsonj  *
8d04ccbb3Scarlsonj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d04ccbb3Scarlsonj  * or http://www.opensolaris.org/os/licensing.
10d04ccbb3Scarlsonj  * See the License for the specific language governing permissions
11d04ccbb3Scarlsonj  * and limitations under the License.
12d04ccbb3Scarlsonj  *
13d04ccbb3Scarlsonj  * When distributing Covered Code, include this CDDL HEADER in each
14d04ccbb3Scarlsonj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d04ccbb3Scarlsonj  * If applicable, add the following below this CDDL HEADER, with the
16d04ccbb3Scarlsonj  * fields enclosed by brackets "[]" replaced with your own identifying
17d04ccbb3Scarlsonj  * information: Portions Copyright [yyyy] [name of copyright owner]
18d04ccbb3Scarlsonj  *
19d04ccbb3Scarlsonj  * CDDL HEADER END
20d04ccbb3Scarlsonj  */
21d04ccbb3Scarlsonj 
22d04ccbb3Scarlsonj /*
23d04ccbb3Scarlsonj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24d04ccbb3Scarlsonj  * Use is subject to license terms.
25d04ccbb3Scarlsonj  */
26d04ccbb3Scarlsonj 
27d04ccbb3Scarlsonj /*
28d04ccbb3Scarlsonj  * This module reads and writes the stable identifier values, DUID and IAID.
29d04ccbb3Scarlsonj  */
30d04ccbb3Scarlsonj 
31d04ccbb3Scarlsonj #include <stdio.h>
32d04ccbb3Scarlsonj #include <stdlib.h>
33d04ccbb3Scarlsonj #include <unistd.h>
34d04ccbb3Scarlsonj #include <string.h>
35d04ccbb3Scarlsonj #include <limits.h>
36d04ccbb3Scarlsonj #include <fcntl.h>
37d04ccbb3Scarlsonj #include <errno.h>
38d04ccbb3Scarlsonj #include <libdlpi.h>
39d04ccbb3Scarlsonj #include <uuid/uuid.h>
40d04ccbb3Scarlsonj #include <sys/types.h>
41d04ccbb3Scarlsonj #include <sys/stat.h>
42d04ccbb3Scarlsonj #include <net/if.h>
43d04ccbb3Scarlsonj #include <netinet/dhcp6.h>
44d04ccbb3Scarlsonj #include <dhcp_inittab.h>
45d04ccbb3Scarlsonj 
46d04ccbb3Scarlsonj #define	DUID_FILE	"/etc/dhcp/duid"
47d04ccbb3Scarlsonj #define	IAID_FILE	"/etc/dhcp/iaid"
48d04ccbb3Scarlsonj 
49d04ccbb3Scarlsonj struct iaid_ent {
50d04ccbb3Scarlsonj 	uint32_t	ie_iaid;
51d04ccbb3Scarlsonj 	char		ie_name[LIFNAMSIZ];
52d04ccbb3Scarlsonj };
53d04ccbb3Scarlsonj 
54d04ccbb3Scarlsonj /*
55d04ccbb3Scarlsonj  * read_stable_duid(): read the system's stable DUID, if any
56d04ccbb3Scarlsonj  *
57d04ccbb3Scarlsonj  *   input: size_t *: pointer to a size_t to return the DUID length
58d04ccbb3Scarlsonj  *  output: uchar_t *: the DUID buffer, or NULL on error (and errno is set)
59d04ccbb3Scarlsonj  *    note: memory returned is from malloc; caller must free.
60d04ccbb3Scarlsonj  */
61d04ccbb3Scarlsonj 
62d04ccbb3Scarlsonj uchar_t *
read_stable_duid(size_t * duidlen)63d04ccbb3Scarlsonj read_stable_duid(size_t *duidlen)
64d04ccbb3Scarlsonj {
65d04ccbb3Scarlsonj 	int fd;
66d04ccbb3Scarlsonj 	ssize_t retv;
67d04ccbb3Scarlsonj 	struct stat sb;
68d04ccbb3Scarlsonj 	uchar_t *duid = NULL;
69d04ccbb3Scarlsonj 
70d04ccbb3Scarlsonj 	if ((fd = open(DUID_FILE, O_RDONLY)) == -1)
71d04ccbb3Scarlsonj 		return (NULL);
72d04ccbb3Scarlsonj 	if (fstat(fd, &sb) != -1 && S_ISREG(sb.st_mode) &&
73d04ccbb3Scarlsonj 	    (duid = malloc(sb.st_size)) != NULL) {
74d04ccbb3Scarlsonj 		retv = read(fd, duid, sb.st_size);
75d04ccbb3Scarlsonj 		if (retv == sb.st_size) {
76d04ccbb3Scarlsonj 			*duidlen = sb.st_size;
77d04ccbb3Scarlsonj 		} else {
78d04ccbb3Scarlsonj 			free(duid);
79d04ccbb3Scarlsonj 			/*
80d04ccbb3Scarlsonj 			 * Make sure that errno always gets set when something
81d04ccbb3Scarlsonj 			 * goes wrong.
82d04ccbb3Scarlsonj 			 */
83d04ccbb3Scarlsonj 			if (retv >= 0)
84d04ccbb3Scarlsonj 				errno = EINVAL;
85d04ccbb3Scarlsonj 			duid = NULL;
86d04ccbb3Scarlsonj 		}
87d04ccbb3Scarlsonj 	}
88d04ccbb3Scarlsonj 	(void) close(fd);
89d04ccbb3Scarlsonj 	return (duid);
90d04ccbb3Scarlsonj }
91d04ccbb3Scarlsonj 
92d04ccbb3Scarlsonj /*
93d04ccbb3Scarlsonj  * write_stable_duid(): write the system's stable DUID.
94d04ccbb3Scarlsonj  *
95d04ccbb3Scarlsonj  *   input: const uchar_t *: pointer to the DUID buffer
96d04ccbb3Scarlsonj  *	    size_t: length of the DUID
97d04ccbb3Scarlsonj  *  output: int: 0 on success, -1 on error.  errno is set on error.
98d04ccbb3Scarlsonj  */
99d04ccbb3Scarlsonj 
100d04ccbb3Scarlsonj int
write_stable_duid(const uchar_t * duid,size_t duidlen)101d04ccbb3Scarlsonj write_stable_duid(const uchar_t *duid, size_t duidlen)
102d04ccbb3Scarlsonj {
103d04ccbb3Scarlsonj 	int fd;
104d04ccbb3Scarlsonj 	ssize_t retv;
105d04ccbb3Scarlsonj 
106d04ccbb3Scarlsonj 	(void) unlink(DUID_FILE);
107d04ccbb3Scarlsonj 	if ((fd = open(DUID_FILE, O_WRONLY | O_CREAT, 0644)) == -1)
108d04ccbb3Scarlsonj 		return (-1);
109d04ccbb3Scarlsonj 	retv = write(fd, duid, duidlen);
110d04ccbb3Scarlsonj 	if (retv == duidlen) {
111d04ccbb3Scarlsonj 		return (close(fd));
112d04ccbb3Scarlsonj 	} else {
113d04ccbb3Scarlsonj 		(void) close(fd);
114d04ccbb3Scarlsonj 		if (retv >= 0)
115d04ccbb3Scarlsonj 			errno = ENOSPC;
116d04ccbb3Scarlsonj 		return (-1);
117d04ccbb3Scarlsonj 	}
118d04ccbb3Scarlsonj }
119d04ccbb3Scarlsonj 
120d04ccbb3Scarlsonj /*
121d04ccbb3Scarlsonj  * make_stable_duid(): create a new DUID
122d04ccbb3Scarlsonj  *
123d04ccbb3Scarlsonj  *   input: const char *: name of physical interface for reference
124d04ccbb3Scarlsonj  *	    size_t *: pointer to a size_t to return the DUID length
125d04ccbb3Scarlsonj  *  output: uchar_t *: the DUID buffer, or NULL on error (and errno is set)
126d04ccbb3Scarlsonj  *    note: memory returned is from malloc; caller must free.
127d04ccbb3Scarlsonj  */
128d04ccbb3Scarlsonj 
129d04ccbb3Scarlsonj uchar_t *
make_stable_duid(const char * physintf,size_t * duidlen)130d04ccbb3Scarlsonj make_stable_duid(const char *physintf, size_t *duidlen)
131d04ccbb3Scarlsonj {
132c7e4935fSss 	int len;
133c7e4935fSss 	dlpi_info_t dlinfo;
134c7e4935fSss 	dlpi_handle_t dh = NULL;
135c7e4935fSss 	uint_t arptype;
136d04ccbb3Scarlsonj 	duid_en_t *den;
137d04ccbb3Scarlsonj 
138d04ccbb3Scarlsonj 	/*
139d04ccbb3Scarlsonj 	 * Try to read the MAC layer address for the physical interface
140d04ccbb3Scarlsonj 	 * provided as a hint.  If that works, we can use a DUID-LLT.
141d04ccbb3Scarlsonj 	 */
142d04ccbb3Scarlsonj 
143c7e4935fSss 	if (dlpi_open(physintf, &dh, 0) == DLPI_SUCCESS &&
144*35b6f047SDavid Höppner 	    dlpi_bind(dh, DLPI_ANY_SAP, NULL) == DLPI_SUCCESS &&
145c7e4935fSss 	    dlpi_info(dh, &dlinfo, 0) == DLPI_SUCCESS &&
146c7e4935fSss 	    (len = dlinfo.di_physaddrlen) > 0 &&
147948f2876Sss 	    (arptype = dlpi_arptype(dlinfo.di_mactype) != 0)) {
148d04ccbb3Scarlsonj 		duid_llt_t *dllt;
149c7e4935fSss 		time_t now;
150d04ccbb3Scarlsonj 
151d04ccbb3Scarlsonj 		if ((dllt = malloc(sizeof (*dllt) + len)) == NULL) {
152c7e4935fSss 			dlpi_close(dh);
153d04ccbb3Scarlsonj 			return (NULL);
154d04ccbb3Scarlsonj 		}
155c7e4935fSss 
156c7e4935fSss 		(void) memcpy((dllt + 1), dlinfo.di_physaddr, len);
157c7e4935fSss 		dllt->dllt_dutype = htons(DHCPV6_DUID_LLT);
158c7e4935fSss 		dllt->dllt_hwtype = htons(arptype);
159c7e4935fSss 		now = time(NULL) - DUID_TIME_BASE;
160c7e4935fSss 		dllt->dllt_time = htonl(now);
161c7e4935fSss 		*duidlen = sizeof (*dllt) + len;
162c7e4935fSss 		dlpi_close(dh);
163c7e4935fSss 		return ((uchar_t *)dllt);
164d04ccbb3Scarlsonj 	}
165c7e4935fSss 	if (dh != NULL)
166c7e4935fSss 		dlpi_close(dh);
167d04ccbb3Scarlsonj 
168d04ccbb3Scarlsonj 	/*
169d04ccbb3Scarlsonj 	 * If we weren't able to create a DUID based on the network interface
170d04ccbb3Scarlsonj 	 * in use, then generate one based on a UUID.
171d04ccbb3Scarlsonj 	 */
172d04ccbb3Scarlsonj 	den = malloc(sizeof (*den) + UUID_LEN);
173d04ccbb3Scarlsonj 	if (den != NULL) {
174d04ccbb3Scarlsonj 		uuid_t uuid;
175d04ccbb3Scarlsonj 
176d04ccbb3Scarlsonj 		den->den_dutype = htons(DHCPV6_DUID_EN);
177d04ccbb3Scarlsonj 		DHCPV6_SET_ENTNUM(den, DHCPV6_SUN_ENT);
178d04ccbb3Scarlsonj 		uuid_generate(uuid);
179d04ccbb3Scarlsonj 		(void) memcpy(den + 1, uuid, UUID_LEN);
180d04ccbb3Scarlsonj 		*duidlen = sizeof (*den) + UUID_LEN;
181d04ccbb3Scarlsonj 	}
182d04ccbb3Scarlsonj 	return ((uchar_t *)den);
183d04ccbb3Scarlsonj }
184d04ccbb3Scarlsonj 
185d04ccbb3Scarlsonj /*
186d04ccbb3Scarlsonj  * read_stable_iaid(): read a link's stable IAID, if any
187d04ccbb3Scarlsonj  *
188d04ccbb3Scarlsonj  *   input: const char *: interface name
189d04ccbb3Scarlsonj  *  output: uint32_t: the IAID, or 0 if none
190d04ccbb3Scarlsonj  */
191d04ccbb3Scarlsonj 
192d04ccbb3Scarlsonj uint32_t
read_stable_iaid(const char * intf)193d04ccbb3Scarlsonj read_stable_iaid(const char *intf)
194d04ccbb3Scarlsonj {
195d04ccbb3Scarlsonj 	int fd;
196d04ccbb3Scarlsonj 	struct iaid_ent ie;
197d04ccbb3Scarlsonj 
198d04ccbb3Scarlsonj 	if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
199d04ccbb3Scarlsonj 		return (0);
200d04ccbb3Scarlsonj 	while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
201d04ccbb3Scarlsonj 		if (strcmp(intf, ie.ie_name) == 0) {
202d04ccbb3Scarlsonj 			(void) close(fd);
203d04ccbb3Scarlsonj 			return (ie.ie_iaid);
204d04ccbb3Scarlsonj 		}
205d04ccbb3Scarlsonj 	}
206d04ccbb3Scarlsonj 	(void) close(fd);
207d04ccbb3Scarlsonj 	return (0);
208d04ccbb3Scarlsonj }
209d04ccbb3Scarlsonj 
210d04ccbb3Scarlsonj /*
211d04ccbb3Scarlsonj  * write_stable_iaid(): write out a link's stable IAID
212d04ccbb3Scarlsonj  *
213d04ccbb3Scarlsonj  *   input: const char *: interface name
214d04ccbb3Scarlsonj  *  output: uint32_t: the IAID, or 0 if none
215d04ccbb3Scarlsonj  */
216d04ccbb3Scarlsonj 
217d04ccbb3Scarlsonj int
write_stable_iaid(const char * intf,uint32_t iaid)218d04ccbb3Scarlsonj write_stable_iaid(const char *intf, uint32_t iaid)
219d04ccbb3Scarlsonj {
220d04ccbb3Scarlsonj 	int fd;
221d04ccbb3Scarlsonj 	struct iaid_ent ie;
222d04ccbb3Scarlsonj 	ssize_t retv;
223d04ccbb3Scarlsonj 
224d04ccbb3Scarlsonj 	if ((fd = open(IAID_FILE, O_RDWR | O_CREAT, 0644)) == -1)
225d04ccbb3Scarlsonj 		return (0);
226d04ccbb3Scarlsonj 	while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
227d04ccbb3Scarlsonj 		if (strcmp(intf, ie.ie_name) == 0) {
228d04ccbb3Scarlsonj 			(void) close(fd);
229d04ccbb3Scarlsonj 			if (iaid == ie.ie_iaid) {
230d04ccbb3Scarlsonj 				return (0);
231d04ccbb3Scarlsonj 			} else {
232d04ccbb3Scarlsonj 				errno = EINVAL;
233d04ccbb3Scarlsonj 				return (-1);
234d04ccbb3Scarlsonj 			}
235d04ccbb3Scarlsonj 		}
236d04ccbb3Scarlsonj 	}
237d04ccbb3Scarlsonj 	(void) memset(&ie, 0, sizeof (ie));
238d04ccbb3Scarlsonj 	ie.ie_iaid = iaid;
239d04ccbb3Scarlsonj 	(void) strlcpy(ie.ie_name, intf, sizeof (ie.ie_name));
240d04ccbb3Scarlsonj 	retv = write(fd, &ie, sizeof (ie));
241d04ccbb3Scarlsonj 	(void) close(fd);
242d04ccbb3Scarlsonj 	if (retv == sizeof (ie)) {
243d04ccbb3Scarlsonj 		return (0);
244d04ccbb3Scarlsonj 	} else {
245d04ccbb3Scarlsonj 		if (retv >= 0)
246d04ccbb3Scarlsonj 			errno = ENOSPC;
247d04ccbb3Scarlsonj 		return (-1);
248d04ccbb3Scarlsonj 	}
249d04ccbb3Scarlsonj }
250d04ccbb3Scarlsonj 
251d04ccbb3Scarlsonj /*
252d04ccbb3Scarlsonj  * make_stable_iaid(): create a stable IAID for a link
253d04ccbb3Scarlsonj  *
254d04ccbb3Scarlsonj  *   input: const char *: interface name
255d04ccbb3Scarlsonj  *	    uint32_t: the ifIndex for this link (as a "hint")
256d04ccbb3Scarlsonj  *  output: uint32_t: the new IAID, never zero
257d04ccbb3Scarlsonj  */
258d04ccbb3Scarlsonj 
259d04ccbb3Scarlsonj /* ARGSUSED */
260d04ccbb3Scarlsonj uint32_t
make_stable_iaid(const char * intf,uint32_t hint)261d04ccbb3Scarlsonj make_stable_iaid(const char *intf, uint32_t hint)
262d04ccbb3Scarlsonj {
263d04ccbb3Scarlsonj 	int fd;
264d04ccbb3Scarlsonj 	struct iaid_ent ie;
265d04ccbb3Scarlsonj 	uint32_t maxid, minunused;
266d04ccbb3Scarlsonj 	boolean_t recheck;
267d04ccbb3Scarlsonj 
268d04ccbb3Scarlsonj 	if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
269d04ccbb3Scarlsonj 		return (hint);
270d04ccbb3Scarlsonj 	maxid = 0;
271d04ccbb3Scarlsonj 	minunused = 1;
272d04ccbb3Scarlsonj 	/*
273d04ccbb3Scarlsonj 	 * This logic is deliberately unoptimized.  The reason is that it runs
274d04ccbb3Scarlsonj 	 * essentially just once per interface for the life of the system.
275d04ccbb3Scarlsonj 	 * Once the IAID is established, there's no reason to generate it
276d04ccbb3Scarlsonj 	 * again, and all we care about here is correctness.  Also, IAIDs tend
277d04ccbb3Scarlsonj 	 * to get added in a logical sequence order, so the outer loop should
278d04ccbb3Scarlsonj 	 * not normally run more than twice.
279d04ccbb3Scarlsonj 	 */
280d04ccbb3Scarlsonj 	do {
281d04ccbb3Scarlsonj 		recheck = B_FALSE;
282d04ccbb3Scarlsonj 		while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
283d04ccbb3Scarlsonj 			if (ie.ie_iaid > maxid)
284d04ccbb3Scarlsonj 				maxid = ie.ie_iaid;
285d04ccbb3Scarlsonj 			if (ie.ie_iaid == minunused) {
286d04ccbb3Scarlsonj 				recheck = B_TRUE;
287d04ccbb3Scarlsonj 				minunused++;
288d04ccbb3Scarlsonj 			}
289d04ccbb3Scarlsonj 			if (ie.ie_iaid == hint)
290d04ccbb3Scarlsonj 				hint = 0;
291d04ccbb3Scarlsonj 		}
292d04ccbb3Scarlsonj 		if (recheck)
293d04ccbb3Scarlsonj 			(void) lseek(fd, 0, SEEK_SET);
294d04ccbb3Scarlsonj 	} while (recheck);
295d04ccbb3Scarlsonj 	(void) close(fd);
296d04ccbb3Scarlsonj 	if (hint != 0)
297d04ccbb3Scarlsonj 		return (hint);
298d04ccbb3Scarlsonj 	else if (maxid != UINT32_MAX)
299d04ccbb3Scarlsonj 		return (maxid + 1);
300d04ccbb3Scarlsonj 	else
301d04ccbb3Scarlsonj 		return (minunused);
302d04ccbb3Scarlsonj }
303