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