view usr/src/lib/libdladm/common/libdllink.c @ 3871:5a1dfce6c5cc

PSARC 2007/140 libdladm restructure 6329535 the use_cache argument of macadm_walk function should be removed 6454340 macadm_walk leaks 6509525 wrong error code when adding/removing ports to/from an aggregation 6509532 aggregation deleting fails but system reports success 6518572 the reference to dls_vlan should be released if setzoneid fails 6520149 show-linkprop stops showing full list of link properties if it fails to display one property 6535220 potential complicated library dependency and code duplication between libdladm and its friends
author yz147064
date Wed, 21 Mar 2007 09:48:58 -0700
parents
children f251acdd9bdc
line wrap: on
line source

/*
 * 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"

#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/dld.h>
#include <libdlpi.h>
#include <libdevinfo.h>
#include <libdllink.h>
#include <libdladm_impl.h>

typedef struct dladm_dev {
	char			dd_name[IFNAMSIZ];
	struct dladm_dev	*dd_next;
} dladm_dev_t;

typedef struct dladm_walk {
	dladm_dev_t		*dw_dev_list;
} dladm_walk_t;

/*
 * Return the attributes of the specified datalink from the DLD driver.
 */
static int
i_dladm_info(int fd, const char *name, dladm_attr_t *dap)
{
	dld_ioc_attr_t	dia;

	if (strlen(name) >= IFNAMSIZ) {
		errno = EINVAL;
		return (-1);
	}

	(void) strlcpy(dia.dia_name, name, IFNAMSIZ);

	if (i_dladm_ioctl(fd, DLDIOCATTR, &dia, sizeof (dia)) < 0)
		return (-1);

	(void) strlcpy(dap->da_dev, dia.dia_dev, MAXNAMELEN);
	dap->da_max_sdu = dia.dia_max_sdu;
	dap->da_vid = dia.dia_vid;

	return (0);
}

/*
 * Adds a datalink to the array corresponding to arg.
 */
static void
i_dladm_nt_net_add(void *arg, char *name)
{
	dladm_walk_t	*dwp = arg;
	dladm_dev_t	*ddp = dwp->dw_dev_list;
	dladm_dev_t	**lastp = &dwp->dw_dev_list;

	while (ddp) {
		/*
		 * Skip duplicates.
		 */
		if (strcmp(ddp->dd_name, name) == 0)
			return;

		lastp = &ddp->dd_next;
		ddp = ddp->dd_next;
	}

	if ((ddp = malloc(sizeof (*ddp))) == NULL)
		return;

	(void) strlcpy(ddp->dd_name, name, IFNAMSIZ);
	ddp->dd_next = NULL;
	*lastp = ddp;
}

/*
 * Walker callback invoked for each DDI_NT_NET node.
 */
static int
i_dladm_nt_net_walk(di_node_t node, di_minor_t minor, void *arg)
{
	char		linkname[DLPI_LINKNAME_MAX];
	dlpi_handle_t	dh;

	if (dlpi_makelink(linkname, di_minor_name(minor),
	    di_instance(node)) != DLPI_SUCCESS)
		return (DI_WALK_CONTINUE);

	if (dlpi_open(linkname, &dh, 0) == DLPI_SUCCESS) {
		i_dladm_nt_net_add(arg, linkname);
		dlpi_close(dh);
	}
	return (DI_WALK_CONTINUE);
}

/*
 * Hold a data-link.
 */
static int
i_dladm_hold_link(const char *name, zoneid_t zoneid, boolean_t docheck)
{
	int		fd;
	dld_hold_vlan_t	dhv;

	if (strlen(name) >= IFNAMSIZ) {
		errno = EINVAL;
		return (-1);
	}

	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
		return (-1);

	bzero(&dhv, sizeof (dld_hold_vlan_t));
	(void) strlcpy(dhv.dhv_name, name, IFNAMSIZ);
	dhv.dhv_zid = zoneid;
	dhv.dhv_docheck = docheck;

	if (i_dladm_ioctl(fd, DLDIOCHOLDVLAN, &dhv, sizeof (dhv)) < 0) {
		int olderrno = errno;

		(void) close(fd);
		errno = olderrno;
		return (-1);
	}

	(void) close(fd);
	return (0);
}

/*
 * Release a data-link.
 */
static int
i_dladm_rele_link(const char *name, zoneid_t zoneid, boolean_t docheck)
{
	int		fd;
	dld_hold_vlan_t	dhv;

	if (strlen(name) >= IFNAMSIZ) {
		errno = EINVAL;
		return (-1);
	}

	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
		return (-1);

	bzero(&dhv, sizeof (dld_hold_vlan_t));
	(void) strlcpy(dhv.dhv_name, name, IFNAMSIZ);
	dhv.dhv_zid = zoneid;
	dhv.dhv_docheck = docheck;

	if (i_dladm_ioctl(fd, DLDIOCRELEVLAN, &dhv, sizeof (dhv)) < 0) {
		int olderrno = errno;

		(void) close(fd);
		errno = olderrno;
		return (-1);
	}

	(void) close(fd);
	return (0);
}

/*
 * Invoke the specified callback function for each active DDI_NT_NET
 * node.
 */
int
dladm_walk(void (*fn)(void *, const char *), void *arg)
{
	di_node_t	root;
	dladm_walk_t	dw;
	dladm_dev_t	*ddp, *last_ddp;

	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
		errno = EFAULT;
		return (-1);
	}
	dw.dw_dev_list = NULL;

	(void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dw,
	    i_dladm_nt_net_walk);

	di_fini(root);

	ddp = dw.dw_dev_list;
	while (ddp) {
		fn(arg, ddp->dd_name);
		last_ddp = ddp;
		ddp = ddp->dd_next;
		free(last_ddp);
	}

	return (0);
}

/*
 * MAC Administration Library.
 *
 * This library is used by administration tools such as dladm(1M) to
 * iterate through the list of MAC interfaces
 *
 */

typedef struct dladm_mac_dev {
	char			dm_name[MAXNAMELEN];
	struct dladm_mac_dev	*dm_next;
} dladm_mac_dev_t;

typedef struct macadm_walk {
	dladm_mac_dev_t		*dmd_dev_list;
} dladm_mac_walk_t;

/*
 * Local callback invoked for each DDI_NT_NET node.
 */
/* ARGSUSED */
static int
i_dladm_mac_walk(di_node_t node, di_minor_t minor, void *arg)
{
	dladm_mac_walk_t	*dmwp = arg;
	dladm_mac_dev_t		*dmdp = dmwp->dmd_dev_list;
	dladm_mac_dev_t		**last_dmdp = &dmwp->dmd_dev_list;
	char			mac[MAXNAMELEN];

	(void) snprintf(mac, MAXNAMELEN, "%s%d",
	    di_driver_name(node), di_instance(node));

	/*
	 * Skip aggregations.
	 */
	if (strcmp("aggr", di_driver_name(node)) == 0)
		return (DI_WALK_CONTINUE);

	while (dmdp) {
		/*
		 * Skip duplicates.
		 */
		if (strcmp(dmdp->dm_name, mac) == 0)
			return (DI_WALK_CONTINUE);

		last_dmdp = &dmdp->dm_next;
		dmdp = dmdp->dm_next;
	}

	if ((dmdp = malloc(sizeof (*dmdp))) == NULL)
		return (DI_WALK_CONTINUE);

	(void) strlcpy(dmdp->dm_name, mac, MAXNAMELEN);
	dmdp->dm_next = NULL;
	*last_dmdp = dmdp;

	return (DI_WALK_CONTINUE);
}

/*
 * Invoke the specified callback for each DDI_NT_MAC node.
 */
int
dladm_mac_walk(void (*fn)(void *, const char *), void *arg)
{
	di_node_t		root;
	dladm_mac_walk_t	dmw;
	dladm_mac_dev_t		*dmdp, *next;

	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
		return (-1);

	dmw.dmd_dev_list = NULL;

	(void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dmw,
	    i_dladm_mac_walk);

	di_fini(root);

	dmdp = dmw.dmd_dev_list;
	for (dmdp = dmw.dmd_dev_list; dmdp != NULL; dmdp = next) {
		next = dmdp->dm_next;
		(*fn)(arg, dmdp->dm_name);
		free(dmdp);
	}

	return (0);
}

/*
 * Returns the current attributes of the specified datalink.
 */
int
dladm_info(const char *name, dladm_attr_t *dap)
{
	int		fd;

	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
		return (-1);

	if (i_dladm_info(fd, name, dap) < 0)
		goto failed;

	(void) close(fd);
	return (0);

failed:
	(void) close(fd);
	return (-1);
}

const char *
dladm_linkstate2str(link_state_t state, char *buf)
{
	const char	*s;

	switch (state) {
	case LINK_STATE_UP:
		s = "up";
		break;
	case LINK_STATE_DOWN:
		s = "down";
		break;
	default:
		s = "unknown";
		break;
	}
	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
	return (buf);
}

const char *
dladm_linkduplex2str(link_duplex_t duplex, char *buf)
{
	const char	*s;

	switch (duplex) {
	case LINK_DUPLEX_FULL:
		s = "full";
		break;
	case LINK_DUPLEX_HALF:
		s = "half";
		break;
	default:
		s = "unknown";
		break;
	}
	(void) snprintf(buf, DLADM_STRSIZE, "%s", s);
	return (buf);
}

/*
 * Do a "hold" operation to a link.
 */
int
dladm_hold_link(const char *name, zoneid_t zoneid, boolean_t docheck)
{
	return (i_dladm_hold_link(name, zoneid, docheck));
}

/*
 * Do a "release" operation to a link.
 */
int
dladm_rele_link(const char *name, zoneid_t zoneid, boolean_t docheck)
{
	return (i_dladm_rele_link(name, zoneid, docheck));
}