OpenSolaris_b135/lib/libdhcpagent/common/dhcp_stable.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * This module reads and writes the stable identifier values, DUID and IAID.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <fcntl.h>
#include <errno.h>
#include <libdlpi.h>
#include <uuid/uuid.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <net/if.h>
#include <netinet/dhcp6.h>
#include <dhcp_inittab.h>

#define	DUID_FILE	"/etc/dhcp/duid"
#define	IAID_FILE	"/etc/dhcp/iaid"

struct iaid_ent {
	uint32_t	ie_iaid;
	char		ie_name[LIFNAMSIZ];
};

/*
 * read_stable_duid(): read the system's stable DUID, if any
 *
 *   input: size_t *: pointer to a size_t to return the DUID length
 *  output: uchar_t *: the DUID buffer, or NULL on error (and errno is set)
 *    note: memory returned is from malloc; caller must free.
 */

uchar_t *
read_stable_duid(size_t *duidlen)
{
	int fd;
	ssize_t retv;
	struct stat sb;
	uchar_t *duid = NULL;

	if ((fd = open(DUID_FILE, O_RDONLY)) == -1)
		return (NULL);
	if (fstat(fd, &sb) != -1 && S_ISREG(sb.st_mode) &&
	    (duid = malloc(sb.st_size)) != NULL) {
		retv = read(fd, duid, sb.st_size);
		if (retv == sb.st_size) {
			*duidlen = sb.st_size;
		} else {
			free(duid);
			/*
			 * Make sure that errno always gets set when something
			 * goes wrong.
			 */
			if (retv >= 0)
				errno = EINVAL;
			duid = NULL;
		}
	}
	(void) close(fd);
	return (duid);
}

/*
 * write_stable_duid(): write the system's stable DUID.
 *
 *   input: const uchar_t *: pointer to the DUID buffer
 *	    size_t: length of the DUID
 *  output: int: 0 on success, -1 on error.  errno is set on error.
 */

int
write_stable_duid(const uchar_t *duid, size_t duidlen)
{
	int fd;
	ssize_t retv;

	(void) unlink(DUID_FILE);
	if ((fd = open(DUID_FILE, O_WRONLY | O_CREAT, 0644)) == -1)
		return (-1);
	retv = write(fd, duid, duidlen);
	if (retv == duidlen) {
		return (close(fd));
	} else {
		(void) close(fd);
		if (retv >= 0)
			errno = ENOSPC;
		return (-1);
	}
}

/*
 * make_stable_duid(): create a new DUID
 *
 *   input: const char *: name of physical interface for reference
 *	    size_t *: pointer to a size_t to return the DUID length
 *  output: uchar_t *: the DUID buffer, or NULL on error (and errno is set)
 *    note: memory returned is from malloc; caller must free.
 */

uchar_t *
make_stable_duid(const char *physintf, size_t *duidlen)
{
	int len;
	dlpi_info_t dlinfo;
	dlpi_handle_t dh = NULL;
	uint_t arptype;
	duid_en_t *den;

	/*
	 * Try to read the MAC layer address for the physical interface
	 * provided as a hint.  If that works, we can use a DUID-LLT.
	 */

	if (dlpi_open(physintf, &dh, 0) == DLPI_SUCCESS &&
	    dlpi_info(dh, &dlinfo, 0) == DLPI_SUCCESS &&
	    (len = dlinfo.di_physaddrlen) > 0 &&
	    (arptype = dlpi_arptype(dlinfo.di_mactype) != 0)) {
		duid_llt_t *dllt;
		time_t now;

		if ((dllt = malloc(sizeof (*dllt) + len)) == NULL) {
			dlpi_close(dh);
			return (NULL);
		}

		(void) memcpy((dllt + 1), dlinfo.di_physaddr, len);
		dllt->dllt_dutype = htons(DHCPV6_DUID_LLT);
		dllt->dllt_hwtype = htons(arptype);
		now = time(NULL) - DUID_TIME_BASE;
		dllt->dllt_time = htonl(now);
		*duidlen = sizeof (*dllt) + len;
		dlpi_close(dh);
		return ((uchar_t *)dllt);
	}
	if (dh != NULL)
		dlpi_close(dh);

	/*
	 * If we weren't able to create a DUID based on the network interface
	 * in use, then generate one based on a UUID.
	 */
	den = malloc(sizeof (*den) + UUID_LEN);
	if (den != NULL) {
		uuid_t uuid;

		den->den_dutype = htons(DHCPV6_DUID_EN);
		DHCPV6_SET_ENTNUM(den, DHCPV6_SUN_ENT);
		uuid_generate(uuid);
		(void) memcpy(den + 1, uuid, UUID_LEN);
		*duidlen = sizeof (*den) + UUID_LEN;
	}
	return ((uchar_t *)den);
}

/*
 * read_stable_iaid(): read a link's stable IAID, if any
 *
 *   input: const char *: interface name
 *  output: uint32_t: the IAID, or 0 if none
 */

uint32_t
read_stable_iaid(const char *intf)
{
	int fd;
	struct iaid_ent ie;

	if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
		return (0);
	while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
		if (strcmp(intf, ie.ie_name) == 0) {
			(void) close(fd);
			return (ie.ie_iaid);
		}
	}
	(void) close(fd);
	return (0);
}

/*
 * write_stable_iaid(): write out a link's stable IAID
 *
 *   input: const char *: interface name
 *  output: uint32_t: the IAID, or 0 if none
 */

int
write_stable_iaid(const char *intf, uint32_t iaid)
{
	int fd;
	struct iaid_ent ie;
	ssize_t retv;

	if ((fd = open(IAID_FILE, O_RDWR | O_CREAT, 0644)) == -1)
		return (0);
	while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
		if (strcmp(intf, ie.ie_name) == 0) {
			(void) close(fd);
			if (iaid == ie.ie_iaid) {
				return (0);
			} else {
				errno = EINVAL;
				return (-1);
			}
		}
	}
	(void) memset(&ie, 0, sizeof (ie));
	ie.ie_iaid = iaid;
	(void) strlcpy(ie.ie_name, intf, sizeof (ie.ie_name));
	retv = write(fd, &ie, sizeof (ie));
	(void) close(fd);
	if (retv == sizeof (ie)) {
		return (0);
	} else {
		if (retv >= 0)
			errno = ENOSPC;
		return (-1);
	}
}

/*
 * make_stable_iaid(): create a stable IAID for a link
 *
 *   input: const char *: interface name
 *	    uint32_t: the ifIndex for this link (as a "hint")
 *  output: uint32_t: the new IAID, never zero
 */

/* ARGSUSED */
uint32_t
make_stable_iaid(const char *intf, uint32_t hint)
{
	int fd;
	struct iaid_ent ie;
	uint32_t maxid, minunused;
	boolean_t recheck;

	if ((fd = open(IAID_FILE, O_RDONLY)) == -1)
		return (hint);
	maxid = 0;
	minunused = 1;
	/*
	 * This logic is deliberately unoptimized.  The reason is that it runs
	 * essentially just once per interface for the life of the system.
	 * Once the IAID is established, there's no reason to generate it
	 * again, and all we care about here is correctness.  Also, IAIDs tend
	 * to get added in a logical sequence order, so the outer loop should
	 * not normally run more than twice.
	 */
	do {
		recheck = B_FALSE;
		while (read(fd, &ie, sizeof (ie)) == sizeof (ie)) {
			if (ie.ie_iaid > maxid)
				maxid = ie.ie_iaid;
			if (ie.ie_iaid == minunused) {
				recheck = B_TRUE;
				minunused++;
			}
			if (ie.ie_iaid == hint)
				hint = 0;
		}
		if (recheck)
			(void) lseek(fd, 0, SEEK_SET);
	} while (recheck);
	(void) close(fd);
	if (hint != 0)
		return (hint);
	else if (maxid != UINT32_MAX)
		return (maxid + 1);
	else
		return (minunused);
}