xref: /onnv-gate/usr/src/lib/libdhcpagent/common/dhcp_stable.c (revision 4456:35ab34adfdad)
13431Scarlsonj /*
23431Scarlsonj  * CDDL HEADER START
33431Scarlsonj  *
43431Scarlsonj  * The contents of this file are subject to the terms of the
53431Scarlsonj  * Common Development and Distribution License (the "License").
63431Scarlsonj  * You may not use this file except in compliance with the License.
73431Scarlsonj  *
83431Scarlsonj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93431Scarlsonj  * or http://www.opensolaris.org/os/licensing.
103431Scarlsonj  * See the License for the specific language governing permissions
113431Scarlsonj  * and limitations under the License.
123431Scarlsonj  *
133431Scarlsonj  * When distributing Covered Code, include this CDDL HEADER in each
143431Scarlsonj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153431Scarlsonj  * If applicable, add the following below this CDDL HEADER, with the
163431Scarlsonj  * fields enclosed by brackets "[]" replaced with your own identifying
173431Scarlsonj  * information: Portions Copyright [yyyy] [name of copyright owner]
183431Scarlsonj  *
193431Scarlsonj  * CDDL HEADER END
203431Scarlsonj  */
213431Scarlsonj 
223431Scarlsonj /*
233431Scarlsonj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
243431Scarlsonj  * Use is subject to license terms.
253431Scarlsonj  */
263431Scarlsonj 
273431Scarlsonj #pragma ident	"%Z%%M%	%I%	%E% SMI"
283431Scarlsonj 
293431Scarlsonj /*
303431Scarlsonj  * This module reads and writes the stable identifier values, DUID and IAID.
313431Scarlsonj  */
323431Scarlsonj 
333431Scarlsonj #include <stdio.h>
343431Scarlsonj #include <stdlib.h>
353431Scarlsonj #include <unistd.h>
363431Scarlsonj #include <string.h>
373431Scarlsonj #include <limits.h>
383431Scarlsonj #include <fcntl.h>
393431Scarlsonj #include <errno.h>
403431Scarlsonj #include <libdlpi.h>
413431Scarlsonj #include <uuid/uuid.h>
423431Scarlsonj #include <sys/types.h>
433431Scarlsonj #include <sys/stat.h>
443431Scarlsonj #include <net/if.h>
453431Scarlsonj #include <netinet/dhcp6.h>
463431Scarlsonj #include <dhcp_inittab.h>
473431Scarlsonj 
483431Scarlsonj #define	DUID_FILE	"/etc/dhcp/duid"
493431Scarlsonj #define	IAID_FILE	"/etc/dhcp/iaid"
503431Scarlsonj 
513431Scarlsonj struct iaid_ent {
523431Scarlsonj 	uint32_t	ie_iaid;
533431Scarlsonj 	char		ie_name[LIFNAMSIZ];
543431Scarlsonj };
553431Scarlsonj 
563431Scarlsonj /*
573431Scarlsonj  * read_stable_duid(): read the system's stable DUID, if any
583431Scarlsonj  *
593431Scarlsonj  *   input: size_t *: pointer to a size_t to return the DUID length
603431Scarlsonj  *  output: uchar_t *: the DUID buffer, or NULL on error (and errno is set)
613431Scarlsonj  *    note: memory returned is from malloc; caller must free.
623431Scarlsonj  */
633431Scarlsonj 
643431Scarlsonj uchar_t *
read_stable_duid(size_t * duidlen)653431Scarlsonj read_stable_duid(size_t *duidlen)
663431Scarlsonj {
673431Scarlsonj 	int fd;
683431Scarlsonj 	ssize_t retv;
693431Scarlsonj 	struct stat sb;
703431Scarlsonj 	uchar_t *duid = NULL;
713431Scarlsonj 
723431Scarlsonj 	if ((fd = open(DUID_FILE, O_RDONLY)) == -1)
733431Scarlsonj 		return (NULL);
743431Scarlsonj 	if (fstat(fd, &sb) != -1 && S_ISREG(sb.st_mode) &&
753431Scarlsonj 	    (duid = malloc(sb.st_size)) != NULL) {
763431Scarlsonj 		retv = read(fd, duid, sb.st_size);
773431Scarlsonj 		if (retv == sb.st_size) {
783431Scarlsonj 			*duidlen = sb.st_size;
793431Scarlsonj 		} else {
803431Scarlsonj 			free(duid);
813431Scarlsonj 			/*
823431Scarlsonj 			 * Make sure that errno always gets set when something
833431Scarlsonj 			 * goes wrong.
843431Scarlsonj 			 */
853431Scarlsonj 			if (retv >= 0)
863431Scarlsonj 				errno = EINVAL;
873431Scarlsonj 			duid = NULL;
883431Scarlsonj 		}
893431Scarlsonj 	}
903431Scarlsonj 	(void) close(fd);
913431Scarlsonj 	return (duid);
923431Scarlsonj }
933431Scarlsonj 
943431Scarlsonj /*
953431Scarlsonj  * write_stable_duid(): write the system's stable DUID.
963431Scarlsonj  *
973431Scarlsonj  *   input: const uchar_t *: pointer to the DUID buffer
983431Scarlsonj  *	    size_t: length of the DUID
993431Scarlsonj  *  output: int: 0 on success, -1 on error.  errno is set on error.
1003431Scarlsonj  */
1013431Scarlsonj 
1023431Scarlsonj int
write_stable_duid(const uchar_t * duid,size_t duidlen)1033431Scarlsonj write_stable_duid(const uchar_t *duid, size_t duidlen)
1043431Scarlsonj {
1053431Scarlsonj 	int fd;
1063431Scarlsonj 	ssize_t retv;
1073431Scarlsonj 
1083431Scarlsonj 	(void) unlink(DUID_FILE);
1093431Scarlsonj 	if ((fd = open(DUID_FILE, O_WRONLY | O_CREAT, 0644)) == -1)
1103431Scarlsonj 		return (-1);
1113431Scarlsonj 	retv = write(fd, duid, duidlen);
1123431Scarlsonj 	if (retv == duidlen) {
1133431Scarlsonj 		return (close(fd));
1143431Scarlsonj 	} else {
1153431Scarlsonj 		(void) close(fd);
1163431Scarlsonj 		if (retv >= 0)
1173431Scarlsonj 			errno = ENOSPC;
1183431Scarlsonj 		return (-1);
1193431Scarlsonj 	}
1203431Scarlsonj }
1213431Scarlsonj 
1223431Scarlsonj /*
1233431Scarlsonj  * make_stable_duid(): create a new DUID
1243431Scarlsonj  *
1253431Scarlsonj  *   input: const char *: name of physical interface for reference
1263431Scarlsonj  *	    size_t *: pointer to a size_t to return the DUID length
1273431Scarlsonj  *  output: uchar_t *: the DUID buffer, or NULL on error (and errno is set)
1283431Scarlsonj  *    note: memory returned is from malloc; caller must free.
1293431Scarlsonj  */
1303431Scarlsonj 
1313431Scarlsonj uchar_t *
make_stable_duid(const char * physintf,size_t * duidlen)1323431Scarlsonj make_stable_duid(const char *physintf, size_t *duidlen)
1333431Scarlsonj {
1343628Sss150715 	int len;
1353628Sss150715 	dlpi_info_t dlinfo;
1363628Sss150715 	dlpi_handle_t dh = NULL;
1373628Sss150715 	uint_t arptype;
1383431Scarlsonj 	duid_en_t *den;
1393431Scarlsonj 
1403431Scarlsonj 	/*
1413431Scarlsonj 	 * Try to read the MAC layer address for the physical interface
1423431Scarlsonj 	 * provided as a hint.  If that works, we can use a DUID-LLT.
1433431Scarlsonj 	 */
1443431Scarlsonj 
1453628Sss150715 	if (dlpi_open(physintf, &dh, 0) == DLPI_SUCCESS &&
1463628Sss150715 	    dlpi_info(dh, &dlinfo, 0) == DLPI_SUCCESS &&
1473628Sss150715 	    (len = dlinfo.di_physaddrlen) > 0 &&
148*4456Sss150715 	    (arptype = dlpi_arptype(dlinfo.di_mactype) != 0)) {
1493431Scarlsonj 		duid_llt_t *dllt;
1503628Sss150715 		time_t now;
1513431Scarlsonj 
1523431Scarlsonj 		if ((dllt = malloc(sizeof (*dllt) + len)) == NULL) {
1533628Sss150715 			dlpi_close(dh);
1543431Scarlsonj 			return (NULL);
1553431Scarlsonj 		}
1563431Scarlsonj 
1573628Sss150715 		(void) memcpy((dllt + 1), dlinfo.di_physaddr, len);
1583628Sss150715 		dllt->dllt_dutype = htons(DHCPV6_DUID_LLT);
1593628Sss150715 		dllt->dllt_hwtype = htons(arptype);
1603628Sss150715 		now = time(NULL) - DUID_TIME_BASE;
1613628Sss150715 		dllt->dllt_time = htonl(now);
1623628Sss150715 		*duidlen = sizeof (*dllt) + len;
1633628Sss150715 		dlpi_close(dh);
1643628Sss150715 		return ((uchar_t *)dllt);
1653431Scarlsonj 	}
1663628Sss150715 	if (dh != NULL)
1673628Sss150715 		dlpi_close(dh);
1683431Scarlsonj 
1693431Scarlsonj 	/*
1703431Scarlsonj 	 * If we weren't able to create a DUID based on the network interface
1713431Scarlsonj 	 * in use, then generate one based on a UUID.
1723431Scarlsonj 	 */
1733431Scarlsonj 	den = malloc(sizeof (*den) + UUID_LEN);
1743431Scarlsonj 	if (den != NULL) {
1753431Scarlsonj 		uuid_t uuid;
1763431Scarlsonj 
1773431Scarlsonj 		den->den_dutype = htons(DHCPV6_DUID_EN);
1783431Scarlsonj 		DHCPV6_SET_ENTNUM(den, DHCPV6_SUN_ENT);
1793431Scarlsonj 		uuid_generate(uuid);
1803431Scarlsonj 		(void) memcpy(den + 1, uuid, UUID_LEN);
1813431Scarlsonj 		*duidlen = sizeof (*den) + UUID_LEN;
1823431Scarlsonj 	}
1833431Scarlsonj 	return ((uchar_t *)den);
1843431Scarlsonj }
1853431Scarlsonj 
1863431Scarlsonj /*
1873431Scarlsonj  * read_stable_iaid(): read a link's stable IAID, if any
1883431Scarlsonj  *
1893431Scarlsonj  *   input: const char *: interface name
1903431Scarlsonj  *  output: uint32_t: the IAID, or 0 if none
1913431Scarlsonj  */
1923431Scarlsonj 
1933431Scarlsonj uint32_t
read_stable_iaid(const char * intf)1943431Scarlsonj read_stable_iaid(const char *intf)
1953431Scarlsonj {
1963431Scarlsonj 	int fd;
1973431Scarlsonj 	struct iaid_ent ie;
1983431Scarlsonj 
1993431Scarlsonj 	if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
2003431Scarlsonj 		return (0);
2013431Scarlsonj 	while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
2023431Scarlsonj 		if (strcmp(intf, ie.ie_name) == 0) {
2033431Scarlsonj 			(void) close(fd);
2043431Scarlsonj 			return (ie.ie_iaid);
2053431Scarlsonj 		}
2063431Scarlsonj 	}
2073431Scarlsonj 	(void) close(fd);
2083431Scarlsonj 	return (0);
2093431Scarlsonj }
2103431Scarlsonj 
2113431Scarlsonj /*
2123431Scarlsonj  * write_stable_iaid(): write out a link's stable IAID
2133431Scarlsonj  *
2143431Scarlsonj  *   input: const char *: interface name
2153431Scarlsonj  *  output: uint32_t: the IAID, or 0 if none
2163431Scarlsonj  */
2173431Scarlsonj 
2183431Scarlsonj int
write_stable_iaid(const char * intf,uint32_t iaid)2193431Scarlsonj write_stable_iaid(const char *intf, uint32_t iaid)
2203431Scarlsonj {
2213431Scarlsonj 	int fd;
2223431Scarlsonj 	struct iaid_ent ie;
2233431Scarlsonj 	ssize_t retv;
2243431Scarlsonj 
2253431Scarlsonj 	if ((fd = open(IAID_FILE, O_RDWR | O_CREAT, 0644)) == -1)
2263431Scarlsonj 		return (0);
2273431Scarlsonj 	while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
2283431Scarlsonj 		if (strcmp(intf, ie.ie_name) == 0) {
2293431Scarlsonj 			(void) close(fd);
2303431Scarlsonj 			if (iaid == ie.ie_iaid) {
2313431Scarlsonj 				return (0);
2323431Scarlsonj 			} else {
2333431Scarlsonj 				errno = EINVAL;
2343431Scarlsonj 				return (-1);
2353431Scarlsonj 			}
2363431Scarlsonj 		}
2373431Scarlsonj 	}
2383431Scarlsonj 	(void) memset(&ie, 0, sizeof (ie));
2393431Scarlsonj 	ie.ie_iaid = iaid;
2403431Scarlsonj 	(void) strlcpy(ie.ie_name, intf, sizeof (ie.ie_name));
2413431Scarlsonj 	retv = write(fd, &ie, sizeof (ie));
2423431Scarlsonj 	(void) close(fd);
2433431Scarlsonj 	if (retv == sizeof (ie)) {
2443431Scarlsonj 		return (0);
2453431Scarlsonj 	} else {
2463431Scarlsonj 		if (retv >= 0)
2473431Scarlsonj 			errno = ENOSPC;
2483431Scarlsonj 		return (-1);
2493431Scarlsonj 	}
2503431Scarlsonj }
2513431Scarlsonj 
2523431Scarlsonj /*
2533431Scarlsonj  * make_stable_iaid(): create a stable IAID for a link
2543431Scarlsonj  *
2553431Scarlsonj  *   input: const char *: interface name
2563431Scarlsonj  *	    uint32_t: the ifIndex for this link (as a "hint")
2573431Scarlsonj  *  output: uint32_t: the new IAID, never zero
2583431Scarlsonj  */
2593431Scarlsonj 
2603431Scarlsonj /* ARGSUSED */
2613431Scarlsonj uint32_t
make_stable_iaid(const char * intf,uint32_t hint)2623431Scarlsonj make_stable_iaid(const char *intf, uint32_t hint)
2633431Scarlsonj {
2643431Scarlsonj 	int fd;
2653431Scarlsonj 	struct iaid_ent ie;
2663431Scarlsonj 	uint32_t maxid, minunused;
2673431Scarlsonj 	boolean_t recheck;
2683431Scarlsonj 
2693431Scarlsonj 	if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
2703431Scarlsonj 		return (hint);
2713431Scarlsonj 	maxid = 0;
2723431Scarlsonj 	minunused = 1;
2733431Scarlsonj 	/*
2743431Scarlsonj 	 * This logic is deliberately unoptimized.  The reason is that it runs
2753431Scarlsonj 	 * essentially just once per interface for the life of the system.
2763431Scarlsonj 	 * Once the IAID is established, there's no reason to generate it
2773431Scarlsonj 	 * again, and all we care about here is correctness.  Also, IAIDs tend
2783431Scarlsonj 	 * to get added in a logical sequence order, so the outer loop should
2793431Scarlsonj 	 * not normally run more than twice.
2803431Scarlsonj 	 */
2813431Scarlsonj 	do {
2823431Scarlsonj 		recheck = B_FALSE;
2833431Scarlsonj 		while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
2843431Scarlsonj 			if (ie.ie_iaid > maxid)
2853431Scarlsonj 				maxid = ie.ie_iaid;
2863431Scarlsonj 			if (ie.ie_iaid == minunused) {
2873431Scarlsonj 				recheck = B_TRUE;
2883431Scarlsonj 				minunused++;
2893431Scarlsonj 			}
2903431Scarlsonj 			if (ie.ie_iaid == hint)
2913431Scarlsonj 				hint = 0;
2923431Scarlsonj 		}
2933431Scarlsonj 		if (recheck)
2943431Scarlsonj 			(void) lseek(fd, 0, SEEK_SET);
2953431Scarlsonj 	} while (recheck);
2963431Scarlsonj 	(void) close(fd);
2973431Scarlsonj 	if (hint != 0)
2983431Scarlsonj 		return (hint);
2993431Scarlsonj 	else if (maxid != UINT32_MAX)
3003431Scarlsonj 		return (maxid + 1);
3013431Scarlsonj 	else
3023431Scarlsonj 		return (minunused);
3033431Scarlsonj }
304