1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 #include <sys/types.h>
  26 #include <unistd.h>
  27 #include <errno.h>
  28 #include <fcntl.h>
  29 #include <assert.h>
  30 #include <ctype.h>
  31 #include <strings.h>
  32 #include <sys/stat.h>
  33 #include <sys/dld.h>
  34 #include <sys/vlan.h>
  35 #include <zone.h>
  36 #include <librcm.h>
  37 #include <libdlpi.h>
  38 #include <libdevinfo.h>
  39 #include <libdlaggr.h>
  40 #include <libdlvlan.h>
  41 #include <libdlvnic.h>
  42 #include <libdlib.h>
  43 #include <libdllink.h>
  44 #include <libdlmgmt.h>
  45 #include <libdladm_impl.h>
  46 #include <libinetutil.h>
  47 
  48 /*
  49  * Return the attributes of the specified datalink from the DLD driver.
  50  */
  51 static dladm_status_t
  52 i_dladm_info(dladm_handle_t handle, const datalink_id_t linkid,
  53     dladm_attr_t *dap)
  54 {
  55         dld_ioc_attr_t  dia;
  56 
  57         dia.dia_linkid = linkid;
  58 
  59         if (ioctl(dladm_dld_fd(handle), DLDIOC_ATTR, &dia) < 0)
  60                 return (dladm_errno2status(errno));
  61 
  62         dap->da_max_sdu = dia.dia_max_sdu;
  63 
  64         return (DLADM_STATUS_OK);
  65 }
  66 
  67 static dladm_status_t
  68 dladm_usagelog(dladm_handle_t handle, dladm_logtype_t type,
  69     dld_ioc_usagelog_t *log_info)
  70 {
  71         if (type == DLADM_LOGTYPE_FLOW)
  72                 log_info->ul_type = MAC_LOGTYPE_FLOW;
  73         else
  74                 log_info->ul_type = MAC_LOGTYPE_LINK;
  75 
  76         if (ioctl(dladm_dld_fd(handle), DLDIOC_USAGELOG, log_info) < 0)
  77                 return (DLADM_STATUS_IOERR);
  78 
  79         return (DLADM_STATUS_OK);
  80 }
  81 
  82 dladm_status_t
  83 dladm_start_usagelog(dladm_handle_t handle, dladm_logtype_t type,
  84     uint_t interval)
  85 {
  86         dld_ioc_usagelog_t      log_info;
  87 
  88         log_info.ul_onoff = B_TRUE;
  89         log_info.ul_interval = interval;
  90 
  91         return (dladm_usagelog(handle, type, &log_info));
  92 }
  93 
  94 dladm_status_t
  95 dladm_stop_usagelog(dladm_handle_t handle, dladm_logtype_t type)
  96 {
  97         dld_ioc_usagelog_t      log_info;
  98 
  99         log_info.ul_onoff = B_FALSE;
 100         log_info.ul_interval = 0;
 101 
 102         return (dladm_usagelog(handle, type, &log_info));
 103 }
 104 
 105 struct i_dladm_walk_arg {
 106         dladm_walkcb_t *fn;
 107         void *arg;
 108 };
 109 
 110 static int
 111 i_dladm_walk(dladm_handle_t handle, datalink_id_t linkid, void *arg)
 112 {
 113         struct i_dladm_walk_arg *walk_arg = arg;
 114         char link[MAXLINKNAMELEN];
 115 
 116         if (dladm_datalink_id2info(handle, linkid, NULL, NULL, NULL, link,
 117             sizeof (link)) == DLADM_STATUS_OK) {
 118                 return (walk_arg->fn(link, walk_arg->arg));
 119         }
 120 
 121         return (DLADM_WALK_CONTINUE);
 122 }
 123 
 124 /*
 125  * Walk all datalinks.
 126  */
 127 dladm_status_t
 128 dladm_walk(dladm_walkcb_t *fn, dladm_handle_t handle, void *arg,
 129     datalink_class_t class, datalink_media_t dmedia, uint32_t flags)
 130 {
 131         struct i_dladm_walk_arg walk_arg;
 132 
 133         walk_arg.fn = fn;
 134         walk_arg.arg = arg;
 135         return (dladm_walk_datalink_id(i_dladm_walk, handle, &walk_arg,
 136             class, dmedia, flags));
 137 }
 138 
 139 #define MAXGRPPERLINK   64
 140 
 141 int
 142 dladm_walk_hwgrp(dladm_handle_t handle, datalink_id_t linkid, void *arg,
 143     boolean_t (*fn)(void *, dladm_hwgrp_attr_t *))
 144 {
 145         int             bufsize, ret;
 146         int             nhwgrp = MAXGRPPERLINK;
 147         dld_ioc_hwgrpget_t *iomp = NULL;
 148 
 149         bufsize = sizeof (dld_ioc_hwgrpget_t) +
 150             nhwgrp * sizeof (dld_hwgrpinfo_t);
 151 
 152         if ((iomp = (dld_ioc_hwgrpget_t *)calloc(1, bufsize)) == NULL)
 153                 return (-1);
 154 
 155         iomp->dih_size = nhwgrp * sizeof (dld_hwgrpinfo_t);
 156         iomp->dih_linkid = linkid;
 157 
 158         ret = ioctl(dladm_dld_fd(handle), DLDIOC_GETHWGRP, iomp);
 159         if (ret == 0) {
 160                 int                     i;
 161                 int                     j;
 162                 dld_hwgrpinfo_t         *dhip;
 163                 dladm_hwgrp_attr_t      attr;
 164 
 165                 dhip = (dld_hwgrpinfo_t *)(iomp + 1);
 166                 for (i = 0; i < iomp->dih_n_groups; i++) {
 167                         bzero(&attr, sizeof (attr));
 168 
 169                         (void) strlcpy(attr.hg_link_name,
 170                             dhip->dhi_link_name, sizeof (attr.hg_link_name));
 171                         attr.hg_grp_num = dhip->dhi_grp_num;
 172                         attr.hg_grp_type = dhip->dhi_grp_type;
 173                         attr.hg_n_rings = dhip->dhi_n_rings;
 174                         for (j = 0; j < dhip->dhi_n_rings; j++)
 175                                 attr.hg_rings[j] = dhip->dhi_rings[j];
 176                         dladm_sort_index_list(attr.hg_rings, attr.hg_n_rings);
 177                         attr.hg_n_clnts = dhip->dhi_n_clnts;
 178                         (void) strlcpy(attr.hg_client_names,
 179                             dhip->dhi_clnts, sizeof (attr.hg_client_names));
 180 
 181                         if (!(*fn)(arg, &attr))
 182                                 break;
 183                         dhip++;
 184                 }
 185         }
 186         free(iomp);
 187         return (ret);
 188 }
 189 
 190 /*
 191  * Invoke the specified callback for each MAC address entry defined on
 192  * the specified device.
 193  */
 194 int
 195 dladm_walk_macaddr(dladm_handle_t handle, datalink_id_t linkid, void *arg,
 196     boolean_t (*fn)(void *, dladm_macaddr_attr_t *))
 197 {
 198         int             bufsize, ret;
 199         int             nmacaddr = 1024;
 200         dld_ioc_macaddrget_t *iomp = NULL;
 201 
 202         bufsize = sizeof (dld_ioc_macaddrget_t) +
 203             nmacaddr * sizeof (dld_macaddrinfo_t);
 204 
 205         if ((iomp = (dld_ioc_macaddrget_t *)calloc(1, bufsize)) == NULL)
 206                 return (-1);
 207 
 208         iomp->dig_size = nmacaddr * sizeof (dld_macaddrinfo_t);
 209         iomp->dig_linkid = linkid;
 210 
 211         ret = ioctl(dladm_dld_fd(handle), DLDIOC_MACADDRGET, iomp);
 212         if (ret == 0) {
 213                 int i;
 214                 dld_macaddrinfo_t *dmip;
 215                 dladm_macaddr_attr_t attr;
 216 
 217                 dmip = (dld_macaddrinfo_t *)(iomp + 1);
 218                 for (i = 0; i < iomp->dig_count; i++) {
 219                         bzero(&attr, sizeof (attr));
 220 
 221                         attr.ma_slot = dmip->dmi_slot;
 222                         attr.ma_flags = 0;
 223                         if (dmip->dmi_flags & DLDIOCMACADDR_USED)
 224                                 attr.ma_flags |= DLADM_MACADDR_USED;
 225                         bcopy(dmip->dmi_addr, attr.ma_addr,
 226                             dmip->dmi_addrlen);
 227                         attr.ma_addrlen = dmip->dmi_addrlen;
 228                         (void) strlcpy(attr.ma_client_name,
 229                             dmip->dmi_client_name, MAXNAMELEN);
 230                         attr.ma_client_linkid = dmip->dma_client_linkid;
 231 
 232                         if (!(*fn)(arg, &attr))
 233                                 break;
 234                         dmip++;
 235                 }
 236         }
 237         free(iomp);
 238         return (ret);
 239 }
 240 
 241 /*
 242  * These routines are used by administration tools such as dladm(1M) to
 243  * iterate through the list of MAC interfaces
 244  */
 245 
 246 typedef struct dladm_mac_dev {
 247         char                    dm_name[MAXNAMELEN];
 248         struct dladm_mac_dev    *dm_next;
 249 } dladm_mac_dev_t;
 250 
 251 typedef struct macadm_walk {
 252         dladm_mac_dev_t  *dmd_dev_list;
 253 } dladm_mac_walk_t;
 254 
 255 /*
 256  * Local callback invoked for each DDI_NT_NET node.
 257  */
 258 /* ARGSUSED */
 259 static int
 260 i_dladm_mac_walk(di_node_t node, di_minor_t minor, void *arg)
 261 {
 262         dladm_mac_walk_t        *dmwp = arg;
 263         dladm_mac_dev_t         *dmdp = dmwp->dmd_dev_list;
 264         dladm_mac_dev_t         **last_dmdp = &dmwp->dmd_dev_list;
 265         char                    mac[MAXNAMELEN];
 266 
 267         (void) snprintf(mac, MAXNAMELEN, "%s%d",
 268             di_driver_name(node), di_instance(node));
 269 
 270         /*
 271          * Skip aggregations.
 272          */
 273         if (strcmp("aggr", di_driver_name(node)) == 0)
 274                 return (DI_WALK_CONTINUE);
 275 
 276         /*
 277          * Skip softmacs.
 278          */
 279         if (strcmp("softmac", di_driver_name(node)) == 0)
 280                 return (DI_WALK_CONTINUE);
 281 
 282         while (dmdp) {
 283                 /*
 284                  * Skip duplicates.
 285                  */
 286                 if (strcmp(dmdp->dm_name, mac) == 0)
 287                         return (DI_WALK_CONTINUE);
 288 
 289                 last_dmdp = &dmdp->dm_next;
 290                 dmdp = dmdp->dm_next;
 291         }
 292 
 293         if ((dmdp = malloc(sizeof (*dmdp))) == NULL)
 294                 return (DI_WALK_CONTINUE);
 295 
 296         (void) strlcpy(dmdp->dm_name, mac, MAXNAMELEN);
 297         dmdp->dm_next = NULL;
 298         *last_dmdp = dmdp;
 299 
 300         return (DI_WALK_CONTINUE);
 301 }
 302 
 303 /*
 304  * Invoke the specified callback for each DDI_NT_NET node.
 305  */
 306 dladm_status_t
 307 dladm_mac_walk(int (*fn)(const char *, void *arg), void *arg)
 308 {
 309         di_node_t               root;
 310         dladm_mac_walk_t        dmw;
 311         dladm_mac_dev_t         *dmdp, *next;
 312         boolean_t               done = B_FALSE;
 313 
 314         if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL)
 315                 return (dladm_errno2status(errno));
 316 
 317         dmw.dmd_dev_list = NULL;
 318 
 319         (void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dmw,
 320             i_dladm_mac_walk);
 321 
 322         di_fini(root);
 323 
 324         dmdp = dmw.dmd_dev_list;
 325         for (dmdp = dmw.dmd_dev_list; dmdp != NULL; dmdp = next) {
 326                 next = dmdp->dm_next;
 327                 if (!done &&
 328                     ((*fn)(dmdp->dm_name, arg) == DLADM_WALK_TERMINATE)) {
 329                         done = B_TRUE;
 330                 }
 331                 free(dmdp);
 332         }
 333 
 334         return (DLADM_STATUS_OK);
 335 }
 336 
 337 /*
 338  * Get the current attributes of the specified datalink.
 339  */
 340 dladm_status_t
 341 dladm_info(dladm_handle_t handle, datalink_id_t linkid, dladm_attr_t *dap)
 342 {
 343         return (i_dladm_info(handle, linkid, dap));
 344 }
 345 
 346 const char *
 347 dladm_linkstate2str(link_state_t state, char *buf)
 348 {
 349         const char      *s;
 350 
 351         switch (state) {
 352         case LINK_STATE_UP:
 353                 s = "up";
 354                 break;
 355         case LINK_STATE_DOWN:
 356                 s = "down";
 357                 break;
 358         default:
 359                 s = "unknown";
 360                 break;
 361         }
 362         (void) snprintf(buf, DLADM_STRSIZE, "%s", s);
 363         return (buf);
 364 }
 365 
 366 const char *
 367 dladm_linkduplex2str(link_duplex_t duplex, char *buf)
 368 {
 369         const char      *s;
 370 
 371         switch (duplex) {
 372         case LINK_DUPLEX_FULL:
 373                 s = "full";
 374                 break;
 375         case LINK_DUPLEX_HALF:
 376                 s = "half";
 377                 break;
 378         default:
 379                 s = "unknown";
 380                 break;
 381         }
 382         (void) snprintf(buf, DLADM_STRSIZE, "%s", s);
 383         return (buf);
 384 }
 385 
 386 /*
 387  * Case 1: rename an existing link1 to a link2 that does not exist.
 388  * Result: <linkid1, link2>
 389  */
 390 static dladm_status_t
 391 i_dladm_rename_link_c1(dladm_handle_t handle, datalink_id_t linkid1,
 392     const char *link1, const char *link2, uint32_t flags)
 393 {
 394         dld_ioc_rename_t        dir;
 395         dladm_status_t          status = DLADM_STATUS_OK;
 396 
 397         /*
 398          * Link is currently available. Check to see whether anything is
 399          * holding this link to prevent a rename operation.
 400          */
 401         if (flags & DLADM_OPT_ACTIVE) {
 402                 dir.dir_linkid1 = linkid1;
 403                 dir.dir_linkid2 = DATALINK_INVALID_LINKID;
 404                 (void) strlcpy(dir.dir_link, link2, MAXLINKNAMELEN);
 405 
 406                 if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0) {
 407                         status = dladm_errno2status(errno);
 408                         return (status);
 409                 }
 410         }
 411 
 412         status = dladm_remap_datalink_id(handle, linkid1, link2);
 413         if (status != DLADM_STATUS_OK && (flags & DLADM_OPT_ACTIVE)) {
 414                 (void) strlcpy(dir.dir_link, link1, MAXLINKNAMELEN);
 415                 (void) ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir);
 416         }
 417         return (status);
 418 }
 419 
 420 typedef struct link_hold_arg_s {
 421         datalink_id_t   linkid;
 422         datalink_id_t   holder;
 423         uint32_t        flags;
 424 } link_hold_arg_t;
 425 
 426 static int
 427 i_dladm_aggr_link_hold(dladm_handle_t handle, datalink_id_t aggrid, void *arg)
 428 {
 429         link_hold_arg_t         *hold_arg = arg;
 430         dladm_aggr_grp_attr_t   ginfo;
 431         dladm_status_t          status;
 432         int                     i;
 433 
 434         status = dladm_aggr_info(handle, aggrid, &ginfo, hold_arg->flags);
 435         if (status != DLADM_STATUS_OK)
 436                 return (DLADM_WALK_CONTINUE);
 437 
 438         for (i = 0; i < ginfo.lg_nports; i++) {
 439                 if (ginfo.lg_ports[i].lp_linkid == hold_arg->linkid) {
 440                         hold_arg->holder = aggrid;
 441                         return (DLADM_WALK_TERMINATE);
 442                 }
 443         }
 444         return (DLADM_WALK_CONTINUE);
 445 }
 446 
 447 static int
 448 i_dladm_vlan_link_hold(dladm_handle_t handle, datalink_id_t vlanid, void *arg)
 449 {
 450         link_hold_arg_t         *hold_arg = arg;
 451         dladm_vlan_attr_t       vinfo;
 452         dladm_status_t          status;
 453 
 454         status = dladm_vlan_info(handle, vlanid, &vinfo, hold_arg->flags);
 455         if (status != DLADM_STATUS_OK)
 456                 return (DLADM_WALK_CONTINUE);
 457 
 458         if (vinfo.dv_linkid == hold_arg->linkid) {
 459                 hold_arg->holder = vlanid;
 460                 return (DLADM_WALK_TERMINATE);
 461         }
 462         return (DLADM_WALK_CONTINUE);
 463 }
 464 
 465 /*
 466  * Case 2: rename an available physical link link1 to a REMOVED physical link
 467  *     link2.  As a result, link1 directly inherits all datalinks configured
 468  *     over link2 (linkid2).
 469  * Result: <linkid2, link2, link1_phymaj, link1_phyinst, link1_devname,
 470  *     link2_other_attr>
 471  */
 472 static dladm_status_t
 473 i_dladm_rename_link_c2(dladm_handle_t handle, datalink_id_t linkid1,
 474     datalink_id_t linkid2)
 475 {
 476         rcm_handle_t            *rcm_hdl = NULL;
 477         nvlist_t                *nvl = NULL;
 478         link_hold_arg_t         arg;
 479         dld_ioc_rename_t        dir;
 480         dladm_conf_t            conf1, conf2;
 481         char                    devname[MAXLINKNAMELEN];
 482         uint64_t                phymaj, phyinst;
 483         dladm_status_t          status = DLADM_STATUS_OK;
 484 
 485         /*
 486          * First check if linkid1 is associated with any persistent
 487          * aggregations or VLANs. If yes, return BUSY.
 488          */
 489         arg.linkid = linkid1;
 490         arg.holder = DATALINK_INVALID_LINKID;
 491         arg.flags = DLADM_OPT_PERSIST;
 492         (void) dladm_walk_datalink_id(i_dladm_aggr_link_hold, handle, &arg,
 493             DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
 494         if (arg.holder != DATALINK_INVALID_LINKID)
 495                 return (DLADM_STATUS_LINKBUSY);
 496 
 497         arg.flags = DLADM_OPT_PERSIST;
 498         (void) dladm_walk_datalink_id(i_dladm_vlan_link_hold, handle, &arg,
 499             DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
 500         if (arg.holder != DATALINK_INVALID_LINKID)
 501                 return (DLADM_STATUS_LINKBUSY);
 502 
 503         /*
 504          * Send DLDIOC_RENAME to request to rename link1's linkid to
 505          * be linkid2. This will check whether link1 is used by any
 506          * aggregations or VLANs, or is held by any application. If yes,
 507          * return failure.
 508          */
 509         dir.dir_linkid1 = linkid1;
 510         dir.dir_linkid2 = linkid2;
 511         if (ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir) < 0)
 512                 status = dladm_errno2status(errno);
 513 
 514         if (status != DLADM_STATUS_OK) {
 515                 return (status);
 516         }
 517 
 518         /*
 519          * Now change the phymaj, phyinst and devname associated with linkid1
 520          * to be associated with linkid2. Before doing that, the old active
 521          * linkprop of linkid1 should be deleted.
 522          */
 523         (void) dladm_set_linkprop(handle, linkid1, NULL, NULL, 0,
 524             DLADM_OPT_ACTIVE);
 525 
 526         if (((status = dladm_getsnap_conf(handle, linkid1, &conf1)) !=
 527             DLADM_STATUS_OK) ||
 528             ((status = dladm_get_conf_field(handle, conf1, FDEVNAME, devname,
 529             MAXLINKNAMELEN)) != DLADM_STATUS_OK) ||
 530             ((status = dladm_get_conf_field(handle, conf1, FPHYMAJ, &phymaj,
 531             sizeof (uint64_t))) != DLADM_STATUS_OK) ||
 532             ((status = dladm_get_conf_field(handle, conf1, FPHYINST, &phyinst,
 533             sizeof (uint64_t))) != DLADM_STATUS_OK) ||
 534             ((status = dladm_open_conf(handle, linkid2, &conf2)) !=
 535             DLADM_STATUS_OK)) {
 536                 dir.dir_linkid1 = linkid2;
 537                 dir.dir_linkid2 = linkid1;
 538                 (void) dladm_init_linkprop(handle, linkid1, B_FALSE);
 539                 (void) ioctl(dladm_dld_fd(handle), DLDIOC_RENAME, &dir);
 540                 return (status);
 541         }
 542 
 543         dladm_destroy_conf(handle, conf1);
 544         (void) dladm_set_conf_field(handle, conf2, FDEVNAME, DLADM_TYPE_STR,
 545             devname);
 546         (void) dladm_set_conf_field(handle, conf2, FPHYMAJ, DLADM_TYPE_UINT64,
 547             &phymaj);
 548         (void) dladm_set_conf_field(handle, conf2, FPHYINST,
 549             DLADM_TYPE_UINT64, &phyinst);
 550         (void) dladm_write_conf(handle, conf2);
 551         dladm_destroy_conf(handle, conf2);
 552 
 553         /*
 554          * Delete link1 and mark link2 up.
 555          */
 556         (void) dladm_remove_conf(handle, linkid1);
 557         (void) dladm_destroy_datalink_id(handle, linkid1, DLADM_OPT_ACTIVE |
 558             DLADM_OPT_PERSIST);
 559         (void) dladm_up_datalink_id(handle, linkid2);
 560 
 561         /*
 562          * Now generate the RCM_RESOURCE_LINK_NEW sysevent which can be
 563          * consumed by the RCM framework to restore all the datalink and
 564          * IP configuration.
 565          */
 566         status = DLADM_STATUS_FAILED;
 567         if ((nvlist_alloc(&nvl, 0, 0) != 0) ||
 568             (nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid2) != 0)) {
 569                 goto done;
 570         }
 571 
 572         if (rcm_alloc_handle(NULL, 0, NULL, &rcm_hdl) != RCM_SUCCESS)
 573                 goto done;
 574 
 575         if (rcm_notify_event(rcm_hdl, RCM_RESOURCE_LINK_NEW, 0, nvl, NULL) ==
 576             RCM_SUCCESS) {
 577                 status = DLADM_STATUS_OK;
 578         }
 579 
 580 done:
 581         if (rcm_hdl != NULL)
 582                 (void) rcm_free_handle(rcm_hdl);
 583         if (nvl != NULL)
 584                 nvlist_free(nvl);
 585         return (status);
 586 }
 587 
 588 /*
 589  * case 3: rename a non-existent link to a REMOVED physical link.
 590  * Set the removed physical link's device name to link1, so that
 591  * when link1 attaches, it inherits all the link configuration of
 592  * the removed physical link.
 593  */
 594 static dladm_status_t
 595 i_dladm_rename_link_c3(dladm_handle_t handle, const char *link1,
 596     datalink_id_t linkid2)
 597 {
 598         dladm_conf_t    conf;
 599         dladm_status_t  status;
 600 
 601         if (!dladm_valid_linkname(link1))
 602                 return (DLADM_STATUS_LINKINVAL);
 603 
 604         status = dladm_open_conf(handle, linkid2, &conf);
 605         if (status != DLADM_STATUS_OK)
 606                 goto done;
 607 
 608         if ((status = dladm_set_conf_field(handle, conf, FDEVNAME,
 609             DLADM_TYPE_STR, link1)) == DLADM_STATUS_OK) {
 610                 status = dladm_write_conf(handle, conf);
 611         }
 612 
 613         dladm_destroy_conf(handle, conf);
 614 
 615 done:
 616         return (status);
 617 }
 618 
 619 dladm_status_t
 620 dladm_rename_link(dladm_handle_t handle, const char *link1, const char *link2)
 621 {
 622         datalink_id_t           linkid1 = DATALINK_INVALID_LINKID;
 623         datalink_id_t           linkid2 = DATALINK_INVALID_LINKID;
 624         uint32_t                flags1, flags2;
 625         datalink_class_t        class1, class2;
 626         uint32_t                media1, media2;
 627         boolean_t               remphy2 = B_FALSE;
 628         dladm_status_t          status;
 629 
 630         (void) dladm_name2info(handle, link1, &linkid1, &flags1, &class1,
 631             &media1);
 632         if ((dladm_name2info(handle, link2, &linkid2, &flags2, &class2,
 633             &media2) == DLADM_STATUS_OK) && (class2 == DATALINK_CLASS_PHYS) &&
 634             (flags2 == DLADM_OPT_PERSIST)) {
 635                 /*
 636                  * see whether link2 is a removed physical link.
 637                  */
 638                 remphy2 = B_TRUE;
 639         }
 640 
 641         if (linkid1 != DATALINK_INVALID_LINKID) {
 642                 if (linkid2 == DATALINK_INVALID_LINKID) {
 643                         /*
 644                          * case 1: rename an existing link to a link that
 645                          * does not exist.
 646                          */
 647                         status = i_dladm_rename_link_c1(handle, linkid1, link1,
 648                             link2, flags1);
 649                 } else if (remphy2) {
 650                         /*
 651                          * case 2: rename an available link to a REMOVED
 652                          * physical link. Return failure if link1 is not
 653                          * an active physical link.
 654                          */
 655                         if ((class1 != class2) || (media1 != media2) ||
 656                             !(flags1 & DLADM_OPT_ACTIVE)) {
 657                                 status = DLADM_STATUS_BADARG;
 658                         } else {
 659                                 status = i_dladm_rename_link_c2(handle, linkid1,
 660                                     linkid2);
 661                         }
 662                 } else {
 663                         status = DLADM_STATUS_EXIST;
 664                 }
 665         } else if (remphy2) {
 666                 status = i_dladm_rename_link_c3(handle, link1, linkid2);
 667         } else {
 668                 status = DLADM_STATUS_NOTFOUND;
 669         }
 670         return (status);
 671 }
 672 
 673 typedef struct consumer_del_phys_arg_s {
 674         datalink_id_t   linkid;
 675 } consumer_del_phys_arg_t;
 676 
 677 static int
 678 i_dladm_vlan_link_del(dladm_handle_t handle, datalink_id_t vlanid, void *arg)
 679 {
 680         consumer_del_phys_arg_t *del_arg = arg;
 681         dladm_vlan_attr_t       vinfo;
 682         dladm_status_t          status;
 683 
 684         status = dladm_vlan_info(handle, vlanid, &vinfo, DLADM_OPT_PERSIST);
 685         if (status != DLADM_STATUS_OK)
 686                 return (DLADM_WALK_CONTINUE);
 687 
 688         if (vinfo.dv_linkid == del_arg->linkid)
 689                 (void) dladm_vlan_delete(handle, vlanid, DLADM_OPT_PERSIST);
 690         return (DLADM_WALK_CONTINUE);
 691 }
 692 
 693 static int
 694 i_dladm_part_link_del(dladm_handle_t handle, datalink_id_t partid, void *arg)
 695 {
 696         consumer_del_phys_arg_t *del_arg = arg;
 697         dladm_part_attr_t       pinfo;
 698         dladm_status_t          status;
 699 
 700         status = dladm_part_info(handle, partid, &pinfo, DLADM_OPT_PERSIST);
 701         if (status != DLADM_STATUS_OK)
 702                 return (DLADM_WALK_CONTINUE);
 703 
 704         if (pinfo.dia_physlinkid == del_arg->linkid)
 705                 (void) dladm_part_delete(handle, partid, DLADM_OPT_PERSIST);
 706         return (DLADM_WALK_CONTINUE);
 707 }
 708 
 709 static int
 710 i_dladm_aggr_link_del(dladm_handle_t handle, datalink_id_t aggrid, void *arg)
 711 {
 712         consumer_del_phys_arg_t         *del_arg = arg;
 713         dladm_aggr_grp_attr_t           ginfo;
 714         dladm_status_t                  status;
 715         dladm_aggr_port_attr_db_t       port[1];
 716         int                             i;
 717 
 718         status = dladm_aggr_info(handle, aggrid, &ginfo, DLADM_OPT_PERSIST);
 719         if (status != DLADM_STATUS_OK)
 720                 return (DLADM_WALK_CONTINUE);
 721 
 722         for (i = 0; i < ginfo.lg_nports; i++)
 723                 if (ginfo.lg_ports[i].lp_linkid == del_arg->linkid)
 724                         break;
 725 
 726         if (i != ginfo.lg_nports) {
 727                 if (ginfo.lg_nports == 1 && i == 0) {
 728                         consumer_del_phys_arg_t aggr_del_arg;
 729 
 730                         /*
 731                          * First delete all the VLANs on this aggregation, then
 732                          * delete the aggregation itself.
 733                          */
 734                         aggr_del_arg.linkid = aggrid;
 735                         (void) dladm_walk_datalink_id(i_dladm_vlan_link_del,
 736                             handle, &aggr_del_arg, DATALINK_CLASS_VLAN,
 737                             DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
 738                         (void) dladm_aggr_delete(handle, aggrid,
 739                             DLADM_OPT_PERSIST);
 740                 } else {
 741                         port[0].lp_linkid = del_arg->linkid;
 742                         (void) dladm_aggr_remove(handle, aggrid, 1, port,
 743                             DLADM_OPT_PERSIST);
 744                 }
 745         }
 746         return (DLADM_WALK_CONTINUE);
 747 }
 748 
 749 typedef struct del_phys_arg_s {
 750         dladm_status_t  rval;
 751 } del_phys_arg_t;
 752 
 753 static int
 754 i_dladm_phys_delete(dladm_handle_t handle, datalink_id_t linkid, void *arg)
 755 {
 756         uint32_t                flags;
 757         datalink_class_t        class;
 758         uint32_t                media;
 759         dladm_status_t          status = DLADM_STATUS_OK;
 760         del_phys_arg_t          *del_phys_arg = arg;
 761         consumer_del_phys_arg_t del_arg;
 762 
 763         if ((status = dladm_datalink_id2info(handle, linkid, &flags, &class,
 764             &media, NULL, 0)) != DLADM_STATUS_OK) {
 765                 goto done;
 766         }
 767 
 768         /*
 769          * see whether this link is a removed physical link.
 770          */
 771         if ((class != DATALINK_CLASS_PHYS) || !(flags & DLADM_OPT_PERSIST) ||
 772             (flags & DLADM_OPT_ACTIVE)) {
 773                 status = DLADM_STATUS_BADARG;
 774                 goto done;
 775         }
 776 
 777         if (media == DL_ETHER) {
 778                 del_arg.linkid = linkid;
 779                 (void) dladm_walk_datalink_id(i_dladm_aggr_link_del, handle,
 780                     &del_arg, DATALINK_CLASS_AGGR, DATALINK_ANY_MEDIATYPE,
 781                     DLADM_OPT_PERSIST);
 782                 (void) dladm_walk_datalink_id(i_dladm_vlan_link_del, handle,
 783                     &del_arg, DATALINK_CLASS_VLAN, DATALINK_ANY_MEDIATYPE,
 784                     DLADM_OPT_PERSIST);
 785         } else if (media == DL_IB) {
 786                 del_arg.linkid = linkid;
 787                 (void) dladm_walk_datalink_id(i_dladm_part_link_del, handle,
 788                     &del_arg, DATALINK_CLASS_PART, DL_IB, DLADM_OPT_PERSIST);
 789         }
 790 
 791         (void) dladm_remove_conf(handle, linkid);
 792         (void) dladm_destroy_datalink_id(handle, linkid, DLADM_OPT_PERSIST);
 793 done:
 794         del_phys_arg->rval = status;
 795         return (DLADM_WALK_CONTINUE);
 796 }
 797 
 798 dladm_status_t
 799 dladm_phys_delete(dladm_handle_t handle, datalink_id_t linkid)
 800 {
 801         del_phys_arg_t  arg = {DLADM_STATUS_OK};
 802 
 803         if (linkid == DATALINK_ALL_LINKID) {
 804                 (void) dladm_walk_datalink_id(i_dladm_phys_delete, handle, &arg,
 805                     DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE,
 806                     DLADM_OPT_PERSIST);
 807                 return (DLADM_STATUS_OK);
 808         } else {
 809                 (void) i_dladm_phys_delete(handle, linkid, &arg);
 810                 return (arg.rval);
 811         }
 812 }
 813 
 814 dladm_status_t
 815 dladm_phys_info(dladm_handle_t handle, datalink_id_t linkid,
 816     dladm_phys_attr_t *dpap, uint32_t flags)
 817 {
 818         dladm_status_t  status;
 819 
 820         assert(flags == DLADM_OPT_ACTIVE || flags == DLADM_OPT_PERSIST);
 821 
 822         switch (flags) {
 823         case DLADM_OPT_PERSIST: {
 824                 dladm_conf_t    conf;
 825 
 826                 status = dladm_getsnap_conf(handle, linkid, &conf);
 827                 if (status != DLADM_STATUS_OK)
 828                         return (status);
 829 
 830                 status = dladm_get_conf_field(handle, conf, FDEVNAME,
 831                     dpap->dp_dev, MAXLINKNAMELEN);
 832                 dladm_destroy_conf(handle, conf);
 833                 return (status);
 834         }
 835         case DLADM_OPT_ACTIVE: {
 836                 dld_ioc_phys_attr_t     dip;
 837 
 838                 dip.dip_linkid = linkid;
 839                 if (ioctl(dladm_dld_fd(handle), DLDIOC_PHYS_ATTR, &dip) < 0) {
 840                         status = dladm_errno2status(errno);
 841                         return (status);
 842                 }
 843                 dpap->dp_novanity = dip.dip_novanity;
 844                 (void) strlcpy(dpap->dp_dev, dip.dip_dev, MAXLINKNAMELEN);
 845                 return (DLADM_STATUS_OK);
 846         }
 847         default:
 848                 return (DLADM_STATUS_BADARG);
 849         }
 850 }
 851 
 852 typedef struct i_walk_dev_state_s {
 853         const char *devname;
 854         datalink_id_t linkid;
 855         boolean_t found;
 856 } i_walk_dev_state_t;
 857 
 858 int
 859 i_dladm_walk_dev2linkid(dladm_handle_t handle, datalink_id_t linkid, void *arg)
 860 {
 861         dladm_phys_attr_t dpa;
 862         dladm_status_t status;
 863         i_walk_dev_state_t *statep = arg;
 864 
 865         status = dladm_phys_info(handle, linkid, &dpa, DLADM_OPT_PERSIST);
 866         if ((status == DLADM_STATUS_OK) &&
 867             (strcmp(statep->devname, dpa.dp_dev) == 0)) {
 868                 statep->found = B_TRUE;
 869                 statep->linkid = linkid;
 870                 return (DLADM_WALK_TERMINATE);
 871         }
 872         return (DLADM_WALK_CONTINUE);
 873 }
 874 
 875 /*
 876  * Get the linkid from the physical device name.
 877  */
 878 dladm_status_t
 879 dladm_dev2linkid(dladm_handle_t handle, const char *devname,
 880     datalink_id_t *linkidp)
 881 {
 882         i_walk_dev_state_t state;
 883 
 884         state.found = B_FALSE;
 885         state.devname = devname;
 886 
 887         (void) dladm_walk_datalink_id(i_dladm_walk_dev2linkid, handle, &state,
 888             DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE, DLADM_OPT_PERSIST);
 889         if (state.found == B_TRUE) {
 890                 *linkidp = state.linkid;
 891                 return (DLADM_STATUS_OK);
 892         } else {
 893                 return (dladm_errno2status(ENOENT));
 894         }
 895 }
 896 
 897 static int
 898 parse_devname(const char *devname, char *driver, uint_t *ppa, size_t maxlen)
 899 {
 900         char    *cp, *tp;
 901         int     len;
 902 
 903         /*
 904          * device name length must not be 0, and it must end with digit.
 905          */
 906         if (((len = strlen(devname)) == 0) || !isdigit(devname[len - 1]))
 907                 return (EINVAL);
 908 
 909         (void) strlcpy(driver, devname, maxlen);
 910         cp = (char *)&driver[len - 1];
 911 
 912         for (tp = cp; isdigit(*tp); tp--) {
 913                 if (tp <= driver)
 914                         return (EINVAL);
 915         }
 916 
 917         *ppa = atoi(tp + 1);
 918         *(tp + 1) = '\0';
 919         return (0);
 920 }
 921 
 922 dladm_status_t
 923 dladm_linkid2legacyname(dladm_handle_t handle, datalink_id_t linkid, char *dev,
 924     size_t len)
 925 {
 926         char                    devname[MAXLINKNAMELEN];
 927         uint16_t                vid = VLAN_ID_NONE;
 928         datalink_class_t        class;
 929         dladm_status_t          status;
 930 
 931         status = dladm_datalink_id2info(handle, linkid, NULL, &class, NULL,
 932             NULL, 0);
 933         if (status != DLADM_STATUS_OK)
 934                 goto done;
 935 
 936         /*
 937          * If this is a VLAN, we must first determine the class and linkid of
 938          * the link the VLAN has been created over.
 939          */
 940         if (class == DATALINK_CLASS_VLAN) {
 941                 dladm_vlan_attr_t       dva;
 942 
 943                 status = dladm_vlan_info(handle, linkid, &dva,
 944                     DLADM_OPT_ACTIVE);
 945                 if (status != DLADM_STATUS_OK)
 946                         goto done;
 947                 linkid = dva.dv_linkid;
 948                 vid = dva.dv_vid;
 949 
 950                 if ((status = dladm_datalink_id2info(handle, linkid, NULL,
 951                     &class, NULL, NULL, 0)) != DLADM_STATUS_OK) {
 952                         goto done;
 953                 }
 954         }
 955 
 956         switch (class) {
 957         case DATALINK_CLASS_AGGR: {
 958                 dladm_aggr_grp_attr_t   dga;
 959 
 960                 status = dladm_aggr_info(handle, linkid, &dga,
 961                     DLADM_OPT_ACTIVE);
 962                 if (status != DLADM_STATUS_OK)
 963                         goto done;
 964 
 965                 if (dga.lg_key == 0) {
 966                         /*
 967                          * If the key was not specified when the aggregation
 968                          * is created, we cannot guess its /dev node name.
 969                          */
 970                         status = DLADM_STATUS_BADARG;
 971                         goto done;
 972                 }
 973                 (void) snprintf(devname, MAXLINKNAMELEN, "aggr%d", dga.lg_key);
 974                 break;
 975         }
 976         case DATALINK_CLASS_PHYS: {
 977                 dladm_phys_attr_t       dpa;
 978 
 979                 status = dladm_phys_info(handle, linkid, &dpa,
 980                     DLADM_OPT_PERSIST);
 981                 if (status != DLADM_STATUS_OK)
 982                         goto done;
 983 
 984                 (void) strlcpy(devname, dpa.dp_dev, MAXLINKNAMELEN);
 985                 break;
 986         }
 987         default:
 988                 status = DLADM_STATUS_BADARG;
 989                 goto done;
 990         }
 991 
 992         if (vid != VLAN_ID_NONE) {
 993                 char            drv[MAXNAMELEN];
 994                 uint_t          ppa;
 995 
 996                 if (parse_devname(devname, drv, &ppa, MAXNAMELEN) != 0) {
 997                         status = DLADM_STATUS_BADARG;
 998                         goto done;
 999                 }
1000                 if (snprintf(dev, len, "%s%d", drv, vid * 1000 + ppa) >= len)
1001                         status = DLADM_STATUS_TOOSMALL;
1002         } else {
1003                 if (strlcpy(dev, devname, len) >= len)
1004                         status = DLADM_STATUS_TOOSMALL;
1005         }
1006 
1007 done:
1008         return (status);
1009 }
1010 
1011 dladm_status_t
1012 dladm_parselink(const char *dev, char *provider, uint_t *ppa)
1013 {
1014         ifspec_t        ifsp;
1015 
1016         if (dev == NULL || !ifparse_ifspec(dev, &ifsp))
1017                 return (DLADM_STATUS_LINKINVAL);
1018 
1019         if (provider != NULL)
1020                 (void) strlcpy(provider, ifsp.ifsp_devnm, DLPI_LINKNAME_MAX);
1021 
1022         if (ppa != NULL)
1023                 *ppa = ifsp.ifsp_ppa;
1024 
1025         return (DLADM_STATUS_OK);
1026 }