libdllink.c revision 2
/*
* 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
* 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
*/
/*
*/
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <ctype.h>
#include <strings.h>
#include <zone.h>
#include <librcm.h>
#include <libdlpi.h>
#include <libdevinfo.h>
#include <libdlaggr.h>
#include <libdlbridge.h>
#include <libdlvlan.h>
#include <libdlvnic.h>
#include <libdlib.h>
#include <libdliptun.h>
#include <libdllink.h>
#include <libdlmgmt.h>
#include <libdlsim.h>
#include <libdladm_impl.h>
#include <libinetutil.h>
/*
* Return the attributes of the specified datalink from the DLD driver.
*/
static dladm_status_t
{
return (dladm_errno2status(errno));
return (DLADM_STATUS_OK);
}
static dladm_status_t
{
if (type == DLADM_LOGTYPE_FLOW)
else
return (DLADM_STATUS_IOERR);
return (DLADM_STATUS_OK);
}
{
}
{
log_info.ul_interval = 0;
}
struct i_dladm_walk_arg {
void *arg;
};
static int
{
char link[MAXLINKNAMELEN];
sizeof (link)) == DLADM_STATUS_OK) {
}
return (DLADM_WALK_CONTINUE);
}
/*
* Walk all datalinks.
*/
{
struct i_dladm_walk_arg walk_arg;
}
#define MAXGRPPERLINK 64
void
{
int i;
int dsize = 0, buffersize;
return;
goto retry;
}
goto done;
}
done:
if (p_nrxrings) {
if (*rxringlist == NULL) {
*p_nrxrings = 0;
return;
}
*p_nrxrings = nrxrings;
for (i = 0; i < *p_nrxrings; i++)
}
if (p_ntxrings) {
if (*txringlist == NULL) {
*p_ntxrings = 0;
return;
}
*p_ntxrings = ntxrings;
for (i = 0; i < *p_ntxrings; i++)
(*txringlist)[i] =
}
}
void
{
int nhwgrp = MAXGRPPERLINK;
goto done;
goto retry;
}
goto done;
}
done:
if (p_nrxrings)
*p_nrxrings = nrxrings;
if (p_ntxrings)
*p_ntxrings = ntxrings;
}
void
{
return;
}
switch (class) {
case DATALINK_CLASS_PHYS:
case DATALINK_CLASS_AGGR: {
break;
}
case DATALINK_CLASS_VNIC:
case DATALINK_CLASS_VLAN: {
if (status != DLADM_STATUS_OK)
break;
break;
}
}
}
/*
* This function walks thru all the ring groups for a given datalink
* and calls the callback function fn on each group.
*/
int
{
int nhwgrp = MAXGRPPERLINK;
/*
* After a successful call to ioctl(..., DLDIOC_GETHWGRP, iomp),
* iomp points to dld_ioc_hwgrpget_t, followed by information
* about dih_ngroups number of ring groups. Information about each
* ring group is variable in size and depends upon the number of
* rings in the group. Buffer pointed to by iomp looks like -
*
* dld_ioc_hwgrpget_t + <dih_ngroups number of group info>
* each group info is
* dld_hwgrpinfo_t + <(dhi_nrings - 1) number of rings>
*/
return (-1);
goto retry;
}
if (ret == 0) {
int i, j, rsize;
for (i = 0; i < iomp->dih_ngroups; i++) {
rsize = sizeof (dladm_hwgrp_attr_t)
break;
for (j = 0; j < dhip->dhi_nrings; j++)
break;
/*
* Data for each group is of variable size
* and follows the struture dld_hwgrpinfo_t.
*/
increment = sizeof (dld_hwgrpinfo_t) +
}
}
return (ret);
}
/*
* Invoke the specified callback for each MAC address entry defined on
* the specified device.
*/
int
{
int nmacaddr = 1024;
bufsize = sizeof (dld_ioc_macaddrget_t) +
nmacaddr * sizeof (dld_macaddrinfo_t);
return (-1);
if (ret == 0) {
int i;
dmip->dmi_addrlen);
break;
dmip++;
}
}
return (ret);
}
/*
* These routines are 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;
typedef struct macadm_walk {
/*
* Local callback invoked for each DDI_NT_NET node.
*/
/* ARGSUSED */
static int
{
char mac[MAXNAMELEN];
/*
* Skip aggregations.
*/
return (DI_WALK_CONTINUE);
/*
* Skip softmacs.
*/
return (DI_WALK_CONTINUE);
while (dmdp) {
/*
* Skip duplicates.
*/
return (DI_WALK_CONTINUE);
}
return (DI_WALK_CONTINUE);
return (DI_WALK_CONTINUE);
}
/*
* Invoke the specified callback for each DDI_NT_NET node.
*/
{
return (dladm_errno2status(errno));
if (!done &&
}
}
return (DLADM_STATUS_OK);
}
/*
* Get the current attributes of the specified datalink.
*/
{
}
const char *
{
const char *s;
switch (state) {
case LINK_STATE_UP:
s = "up";
break;
case LINK_STATE_DOWN:
s = "down";
break;
default:
s = "unknown";
break;
}
return (buf);
}
const char *
{
const char *s;
switch (duplex) {
case LINK_DUPLEX_FULL:
s = "full";
break;
case LINK_DUPLEX_HALF:
s = "half";
break;
default:
s = "unknown";
break;
}
return (buf);
}
/*
* Case 1: rename an existing link1 to a link2 that does not exist.
* Result: <linkid1, link2>
*/
static dladm_status_t
{
/*
* Link is currently available. Check to see whether anything is
* holding this link to prevent a rename operation.
*/
if (flags & DLADM_OPT_ACTIVE) {
return (status);
}
}
}
return (status);
}
typedef struct link_hold_arg_s {
static int
{
int i;
if (status != DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
return (DLADM_WALK_TERMINATE);
}
}
return (DLADM_WALK_CONTINUE);
}
static int
{
if (status != DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
return (DLADM_WALK_TERMINATE);
}
return (DLADM_WALK_CONTINUE);
}
/*
* Case 2: rename an available physical link link1 to a REMOVED physical link
* link2. As a result, link1 directly inherits all datalinks configured
* over link2 (linkid2).
* Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname,
* link2_other_attr>
*/
static dladm_status_t
{
char devname[MAXLINKNAMELEN];
/*
* First check if linkid1 is associated with any persistent
* aggregations or VLANs. If yes, return BUSY.
*/
return (DLADM_STATUS_LINKBUSY);
return (DLADM_STATUS_LINKBUSY);
/*
* Send DLDIOC_RENAME to request to rename link1's linkid to
* be linkid2. This will check whether link1 is used by any
* aggregations or VLANs, or is held by any application. If yes,
* return failure.
*/
if (status != DLADM_STATUS_OK) {
return (status);
}
/*
* Now change the phymaj, phyinst and devname associated with linkid1
* to be associated with linkid2. Before doing that, the old active
* linkprop of linkid1 should be deleted.
*/
DLADM_STATUS_OK) ||
MAXLINKNAMELEN)) != DLADM_STATUS_OK) ||
sizeof (uint64_t))) != DLADM_STATUS_OK) ||
sizeof (uint64_t))) != DLADM_STATUS_OK) ||
DLADM_STATUS_OK)) {
return (status);
}
devname);
&phymaj);
/*
* Delete link1 and mark link2 up.
*/
/*
* Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be
* consumed by the RCM framework to restore all the datalink and
* IP configuration.
*/
if ((nvlist_alloc(&nvl, 0, 0) != 0) ||
goto done;
}
goto done;
RCM_SUCCESS) {
}
done:
(void) rcm_free_handle(rcm_hdl);
return (status);
}
/*
* case 3: rename a non-existent link to a REMOVED physical link.
* Set the removed physical link's device name to link1, so that
* when link1 attaches, it inherits all the link configuration of
* the removed physical link.
*/
static dladm_status_t
{
if (!dladm_valid_linkname(link1))
return (DLADM_STATUS_LINKINVAL);
if (status != DLADM_STATUS_OK)
goto done;
}
done:
return (status);
}
{
&media1);
(flags2 == DLADM_OPT_PERSIST)) {
/*
* see whether link2 is a removed physical link.
*/
}
if (linkid1 != DATALINK_INVALID_LINKID) {
if (linkid2 == DATALINK_INVALID_LINKID) {
/*
* case 1: rename an existing link to a link that
* does not exist.
*/
} else if (remphy2) {
/*
* case 2: rename an available link to a REMOVED
* physical link. Return failure if link1 is not
* an active physical link.
*/
!(flags1 & DLADM_OPT_ACTIVE)) {
} else {
linkid2);
}
} else {
}
} else if (remphy2) {
} else {
}
return (status);
}
typedef struct consumer_del_phys_arg_s {
static int
{
if (status != DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
return (DLADM_WALK_CONTINUE);
}
static int
{
if (status != DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
return (DLADM_WALK_CONTINUE);
}
static int
{
int i;
if (status != DLADM_STATUS_OK)
return (DLADM_WALK_CONTINUE);
break;
/*
* First delete all the VLANs on this aggregation, then
* delete the aggregation itself.
*/
} else {
}
}
return (DLADM_WALK_CONTINUE);
}
typedef struct del_phys_arg_s {
static int
{
goto done;
}
/*
* see whether this link is a removed physical link.
*/
(flags & DLADM_OPT_ACTIVE)) {
goto done;
}
}
done:
return (DLADM_WALK_CONTINUE);
}
{
if (linkid == DATALINK_ALL_LINKID) {
return (DLADM_STATUS_OK);
} else {
}
}
{
switch (flags) {
case DLADM_OPT_PERSIST: {
if (status != DLADM_STATUS_OK)
return (status);
if (status == DLADM_STATUS_OK) {
}
return (status);
}
case DLADM_OPT_ACTIVE: {
return (status);
}
return (DLADM_STATUS_OK);
}
default:
return (DLADM_STATUS_BADARG);
}
}
typedef struct i_walk_dev_state_s {
const char *devname;
int
{
if ((status == DLADM_STATUS_OK) &&
return (DLADM_WALK_TERMINATE);
}
return (DLADM_WALK_CONTINUE);
}
/*
* Get the linkid from the physical device name.
*/
{
return (DLADM_STATUS_OK);
} else {
return (dladm_errno2status(ENOENT));
}
}
static int
{
int len;
/*
* device name length must not be 0, and it must end with digit.
*/
return (EINVAL);
return (EINVAL);
}
return (0);
}
{
char devname[MAXLINKNAMELEN];
NULL, 0);
if (status != DLADM_STATUS_OK)
goto done;
/*
* If this is a VLAN, we must first determine the class and linkid of
* the link the VLAN has been created over.
*/
if (class == DATALINK_CLASS_VLAN) {
if (status != DLADM_STATUS_OK)
goto done;
goto done;
}
}
switch (class) {
case DATALINK_CLASS_AGGR: {
if (status != DLADM_STATUS_OK)
goto done;
/*
* If the key was not specified when the aggregation
* is created, we cannot guess its /dev node name.
*/
goto done;
}
break;
}
case DATALINK_CLASS_PHYS: {
if (status != DLADM_STATUS_OK)
goto done;
break;
}
default:
goto done;
}
if (vid != VLAN_ID_NONE) {
char drv[MAXNAMELEN];
goto done;
}
} else {
}
done:
return (status);
}
int (*fn)(void *, dladm_cos_attr_t *))
{
return (DLADM_STATUS_NOMEM);
return (dladm_errno2status(err));
}
sizeof (attr.ca_link_name));
sizeof (attr.ca_client_names));
break;
dcp++;
}
return (DLADM_STATUS_OK);
}
/*
* Bring down *all* aggregations, bridges, IP tunnels, IB partitions, simnets,
* VLANs, VNICs.
*/
void
{
/* Take down etherstubs after VNICs have been taken down */
}