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) 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2018 Joyent, Inc.
  24  * Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
  25  */
  26 
  27 #include <sys/types.h>
  28 #include <sys/cred.h>
  29 #include <sys/sysmacros.h>
  30 #include <sys/conf.h>
  31 #include <sys/cmn_err.h>
  32 #include <sys/list.h>
  33 #include <sys/ksynch.h>
  34 #include <sys/kmem.h>
  35 #include <sys/stream.h>
  36 #include <sys/modctl.h>
  37 #include <sys/ddi.h>
  38 #include <sys/sunddi.h>
  39 #include <sys/atomic.h>
  40 #include <sys/stat.h>
  41 #include <sys/modhash.h>
  42 #include <sys/strsubr.h>
  43 #include <sys/strsun.h>
  44 #include <sys/dlpi.h>
  45 #include <sys/mac.h>
  46 #include <sys/mac_provider.h>
  47 #include <sys/mac_client.h>
  48 #include <sys/mac_client_priv.h>
  49 #include <sys/mac_ether.h>
  50 #include <sys/dls.h>
  51 #include <sys/pattr.h>
  52 #include <sys/time.h>
  53 #include <sys/vlan.h>
  54 #include <sys/vnic.h>
  55 #include <sys/vnic_impl.h>
  56 #include <sys/mac_impl.h>
  57 #include <sys/mac_flow_impl.h>
  58 #include <inet/ip_impl.h>
  59 
  60 /*
  61  * Note that for best performance, the VNIC is a passthrough design.
  62  * For each VNIC corresponds a MAC client of the underlying MAC (lower MAC).
  63  * This MAC client is opened by the VNIC driver at VNIC creation,
  64  * and closed when the VNIC is deleted.
  65  * When a MAC client of the VNIC itself opens a VNIC, the MAC layer
  66  * (upper MAC) detects that the MAC being opened is a VNIC. Instead
  67  * of allocating a new MAC client, it asks the VNIC driver to return
  68  * the lower MAC client handle associated with the VNIC, and that handle
  69  * is returned to the upper MAC client directly. This allows access
  70  * by upper MAC clients of the VNIC to have direct access to the lower
  71  * MAC client for the control path and data path.
  72  *
  73  * Due to this passthrough, some of the entry points exported by the
  74  * VNIC driver are never directly invoked. These entry points include
  75  * vnic_m_start, vnic_m_stop, vnic_m_promisc, vnic_m_multicst, etc.
  76  *
  77  * VNICs support multiple upper mac clients to enable support for
  78  * multiple MAC addresses on the VNIC. When the VNIC is created the
  79  * initial mac client is the primary upper mac. Any additional mac
  80  * clients are secondary macs.
  81  */
  82 
  83 static int vnic_m_start(void *);
  84 static void vnic_m_stop(void *);
  85 static int vnic_m_promisc(void *, boolean_t);
  86 static int vnic_m_multicst(void *, boolean_t, const uint8_t *);
  87 static int vnic_m_unicst(void *, const uint8_t *);
  88 static int vnic_m_stat(void *, uint_t, uint64_t *);
  89 static void vnic_m_ioctl(void *, queue_t *, mblk_t *);
  90 static int vnic_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
  91     const void *);
  92 static int vnic_m_getprop(void *, const char *, mac_prop_id_t, uint_t, void *);
  93 static void vnic_m_propinfo(void *, const char *, mac_prop_id_t,
  94     mac_prop_info_handle_t);
  95 static mblk_t *vnic_m_tx(void *, mblk_t *);
  96 static boolean_t vnic_m_capab_get(void *, mac_capab_t, void *);
  97 static void vnic_notify_cb(void *, mac_notify_type_t);
  98 static void vnic_cleanup_secondary_macs(vnic_t *, int);
  99 
 100 static kmem_cache_t     *vnic_cache;
 101 static krwlock_t        vnic_lock;
 102 static uint_t           vnic_count;
 103 
 104 #define ANCHOR_VNIC_MIN_MTU     576
 105 #define ANCHOR_VNIC_MAX_MTU     9000
 106 
 107 /* hash of VNICs (vnic_t's), keyed by VNIC id */
 108 static mod_hash_t       *vnic_hash;
 109 #define VNIC_HASHSZ     64
 110 #define VNIC_HASH_KEY(vnic_id)  ((mod_hash_key_t)(uintptr_t)vnic_id)
 111 
 112 #define VNIC_M_CALLBACK_FLAGS   \
 113         (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO)
 114 
 115 static mac_callbacks_t vnic_m_callbacks = {
 116         VNIC_M_CALLBACK_FLAGS,
 117         vnic_m_stat,
 118         vnic_m_start,
 119         vnic_m_stop,
 120         vnic_m_promisc,
 121         vnic_m_multicst,
 122         vnic_m_unicst,
 123         vnic_m_tx,
 124         NULL,
 125         vnic_m_ioctl,
 126         vnic_m_capab_get,
 127         NULL,
 128         NULL,
 129         vnic_m_setprop,
 130         vnic_m_getprop,
 131         vnic_m_propinfo
 132 };
 133 
 134 void
 135 vnic_dev_init(void)
 136 {
 137         vnic_cache = kmem_cache_create("vnic_cache",
 138             sizeof (vnic_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
 139 
 140         vnic_hash = mod_hash_create_idhash("vnic_hash",
 141             VNIC_HASHSZ, mod_hash_null_valdtor);
 142 
 143         rw_init(&vnic_lock, NULL, RW_DEFAULT, NULL);
 144 
 145         vnic_count = 0;
 146 }
 147 
 148 void
 149 vnic_dev_fini(void)
 150 {
 151         ASSERT(vnic_count == 0);
 152 
 153         rw_destroy(&vnic_lock);
 154         mod_hash_destroy_idhash(vnic_hash);
 155         kmem_cache_destroy(vnic_cache);
 156 }
 157 
 158 uint_t
 159 vnic_dev_count(void)
 160 {
 161         return (vnic_count);
 162 }
 163 
 164 static vnic_ioc_diag_t
 165 vnic_mac2vnic_diag(mac_diag_t diag)
 166 {
 167         switch (diag) {
 168         case MAC_DIAG_MACADDR_NIC:
 169                 return (VNIC_IOC_DIAG_MACADDR_NIC);
 170         case MAC_DIAG_MACADDR_INUSE:
 171                 return (VNIC_IOC_DIAG_MACADDR_INUSE);
 172         case MAC_DIAG_MACADDR_INVALID:
 173                 return (VNIC_IOC_DIAG_MACADDR_INVALID);
 174         case MAC_DIAG_MACADDRLEN_INVALID:
 175                 return (VNIC_IOC_DIAG_MACADDRLEN_INVALID);
 176         case MAC_DIAG_MACFACTORYSLOTINVALID:
 177                 return (VNIC_IOC_DIAG_MACFACTORYSLOTINVALID);
 178         case MAC_DIAG_MACFACTORYSLOTUSED:
 179                 return (VNIC_IOC_DIAG_MACFACTORYSLOTUSED);
 180         case MAC_DIAG_MACFACTORYSLOTALLUSED:
 181                 return (VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED);
 182         case MAC_DIAG_MACFACTORYNOTSUP:
 183                 return (VNIC_IOC_DIAG_MACFACTORYNOTSUP);
 184         case MAC_DIAG_MACPREFIX_INVALID:
 185                 return (VNIC_IOC_DIAG_MACPREFIX_INVALID);
 186         case MAC_DIAG_MACPREFIXLEN_INVALID:
 187                 return (VNIC_IOC_DIAG_MACPREFIXLEN_INVALID);
 188         case MAC_DIAG_MACNO_HWRINGS:
 189                 return (VNIC_IOC_DIAG_NO_HWRINGS);
 190         default:
 191                 return (VNIC_IOC_DIAG_NONE);
 192         }
 193 }
 194 
 195 static int
 196 vnic_unicast_add(vnic_t *vnic, vnic_mac_addr_type_t vnic_addr_type,
 197     int *addr_slot, uint_t prefix_len, int *addr_len_ptr_arg,
 198     uint8_t *mac_addr_arg, uint16_t flags, vnic_ioc_diag_t *diag,
 199     uint16_t vid, boolean_t req_hwgrp_flag)
 200 {
 201         mac_diag_t mac_diag;
 202         uint16_t mac_flags = 0;
 203         int err;
 204         uint_t addr_len;
 205 
 206         if (flags & VNIC_IOC_CREATE_NODUPCHECK)
 207                 mac_flags |= MAC_UNICAST_NODUPCHECK;
 208 
 209         switch (vnic_addr_type) {
 210         case VNIC_MAC_ADDR_TYPE_FIXED:
 211         case VNIC_MAC_ADDR_TYPE_VRID:
 212                 /*
 213                  * The MAC address value to assign to the VNIC
 214                  * is already provided in mac_addr_arg. addr_len_ptr_arg
 215                  * already contains the MAC address length.
 216                  */
 217                 break;
 218 
 219         case VNIC_MAC_ADDR_TYPE_RANDOM:
 220                 /*
 221                  * Random MAC address. There are two sub-cases:
 222                  *
 223                  * 1 - If mac_len == 0, a new MAC address is generated.
 224                  *      The length of the MAC address to generated depends
 225                  *      on the type of MAC used. The prefix to use for the MAC
 226                  *      address is stored in the most significant bytes
 227                  *      of the mac_addr argument, and its length is specified
 228                  *      by the mac_prefix_len argument. This prefix can
 229                  *      correspond to a IEEE OUI in the case of Ethernet,
 230                  *      for example.
 231                  *
 232                  * 2 - If mac_len > 0, the address was already picked
 233                  *      randomly, and is now passed back during VNIC
 234                  *      re-creation. The mac_addr argument contains the MAC
 235                  *      address that was generated. We distinguish this
 236                  *      case from the fixed MAC address case, since we
 237                  *      want the user consumers to know, when they query
 238                  *      the list of VNICs, that a VNIC was assigned a
 239                  *      random MAC address vs assigned a fixed address
 240                  *      specified by the user.
 241                  */
 242 
 243                 /*
 244                  * If it's a pre-generated address, we're done. mac_addr_arg
 245                  * and addr_len_ptr_arg already contain the MAC address
 246                  * value and length.
 247                  */
 248                 if (*addr_len_ptr_arg > 0)
 249                         break;
 250 
 251                 /* generate a new random MAC address */
 252                 if ((err = mac_addr_random(vnic->vn_mch,
 253                     prefix_len, mac_addr_arg, &mac_diag)) != 0) {
 254                         *diag = vnic_mac2vnic_diag(mac_diag);
 255                         return (err);
 256                 }
 257                 *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
 258                 break;
 259 
 260         case VNIC_MAC_ADDR_TYPE_FACTORY:
 261                 err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
 262                 if (err != 0) {
 263                         if (err == EINVAL)
 264                                 *diag = VNIC_IOC_DIAG_MACFACTORYSLOTINVALID;
 265                         if (err == EBUSY)
 266                                 *diag = VNIC_IOC_DIAG_MACFACTORYSLOTUSED;
 267                         if (err == ENOSPC)
 268                                 *diag = VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED;
 269                         return (err);
 270                 }
 271 
 272                 mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
 273                     mac_addr_arg, &addr_len, NULL, NULL);
 274                 *addr_len_ptr_arg = addr_len;
 275                 break;
 276 
 277         case VNIC_MAC_ADDR_TYPE_AUTO:
 278                 /* first try to allocate a factory MAC address */
 279                 err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
 280                 if (err == 0) {
 281                         mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
 282                             mac_addr_arg, &addr_len, NULL, NULL);
 283                         vnic_addr_type = VNIC_MAC_ADDR_TYPE_FACTORY;
 284                         *addr_len_ptr_arg = addr_len;
 285                         break;
 286                 }
 287 
 288                 /*
 289                  * Allocating a factory MAC address failed, generate a
 290                  * random MAC address instead.
 291                  */
 292                 if ((err = mac_addr_random(vnic->vn_mch,
 293                     prefix_len, mac_addr_arg, &mac_diag)) != 0) {
 294                         *diag = vnic_mac2vnic_diag(mac_diag);
 295                         return (err);
 296                 }
 297                 *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
 298                 vnic_addr_type = VNIC_MAC_ADDR_TYPE_RANDOM;
 299                 break;
 300         case VNIC_MAC_ADDR_TYPE_PRIMARY:
 301                 /*
 302                  * We get the address here since we copy it in the
 303                  * vnic's vn_addr.
 304                  * We can't ask for hardware resources since we
 305                  * don't currently support hardware classification
 306                  * for these MAC clients.
 307                  */
 308                 if (req_hwgrp_flag) {
 309                         *diag = VNIC_IOC_DIAG_NO_HWRINGS;
 310                         return (ENOTSUP);
 311                 }
 312                 mac_unicast_primary_get(vnic->vn_lower_mh, mac_addr_arg);
 313                 *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
 314                 mac_flags |= MAC_UNICAST_VNIC_PRIMARY;
 315                 break;
 316         }
 317 
 318         vnic->vn_addr_type = vnic_addr_type;
 319 
 320         err = mac_unicast_add(vnic->vn_mch, mac_addr_arg, mac_flags,
 321             &vnic->vn_muh, vid, &mac_diag);
 322         if (err != 0) {
 323                 if (vnic_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
 324                         /* release factory MAC address */
 325                         mac_addr_factory_release(vnic->vn_mch, *addr_slot);
 326                 }
 327                 *diag = vnic_mac2vnic_diag(mac_diag);
 328         }
 329 
 330         return (err);
 331 }
 332 
 333 /*
 334  * Create a new VNIC upon request from administrator.
 335  * Returns 0 on success, an errno on failure.
 336  */
 337 /* ARGSUSED */
 338 int
 339 vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid,
 340     vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr,
 341     int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid,
 342     int af, mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag,
 343     cred_t *credp)
 344 {
 345         vnic_t *vnic;
 346         mac_register_t *mac;
 347         int err;
 348         boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0);
 349         char vnic_name[MAXNAMELEN];
 350         const mac_info_t *minfop;
 351         uint32_t req_hwgrp_flag = B_FALSE;
 352 
 353         *diag = VNIC_IOC_DIAG_NONE;
 354 
 355         rw_enter(&vnic_lock, RW_WRITER);
 356 
 357         /* Does a VNIC with the same id already exist? */
 358         err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
 359             (mod_hash_val_t *)&vnic);
 360         if (err == 0) {
 361                 rw_exit(&vnic_lock);
 362                 return (EEXIST);
 363         }
 364 
 365         vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP);
 366         if (vnic == NULL) {
 367                 rw_exit(&vnic_lock);
 368                 return (ENOMEM);
 369         }
 370 
 371         bzero(vnic, sizeof (*vnic));
 372 
 373         vnic->vn_ls = LINK_STATE_UNKNOWN;
 374         vnic->vn_id = vnic_id;
 375         vnic->vn_link_id = linkid;
 376         vnic->vn_vrid = vrid;
 377         vnic->vn_af = af;
 378 
 379         if (!is_anchor) {
 380                 if (linkid == DATALINK_INVALID_LINKID) {
 381                         err = EINVAL;
 382                         goto bail;
 383                 }
 384 
 385                 /*
 386                  * Open the lower MAC and assign its initial bandwidth and
 387                  * MAC address. We do this here during VNIC creation and
 388                  * do not wait until the upper MAC client open so that we
 389                  * can validate the VNIC creation parameters (bandwidth,
 390                  * MAC address, etc) and reserve a factory MAC address if
 391                  * one was requested.
 392                  */
 393                 err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh);
 394                 if (err != 0)
 395                         goto bail;
 396 
 397                 /*
 398                  * VNIC(vlan) over VNICs(vlans) is not supported.
 399                  */
 400                 if (mac_is_vnic(vnic->vn_lower_mh)) {
 401                         err = EINVAL;
 402                         goto bail;
 403                 }
 404 
 405                 /* only ethernet support for now */
 406                 minfop = mac_info(vnic->vn_lower_mh);
 407                 if (minfop->mi_nativemedia != DL_ETHER) {
 408                         err = ENOTSUP;
 409                         goto bail;
 410                 }
 411 
 412                 (void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL,
 413                     NULL);
 414                 err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch,
 415                     vnic_name, MAC_OPEN_FLAGS_IS_VNIC);
 416                 if (err != 0)
 417                         goto bail;
 418 
 419                 /* assign a MAC address to the VNIC */
 420 
 421                 err = vnic_unicast_add(vnic, *vnic_addr_type, mac_slot,
 422                     mac_prefix_len, mac_len, mac_addr, flags, diag, vid,
 423                     req_hwgrp_flag);
 424                 if (err != 0) {
 425                         vnic->vn_muh = NULL;
 426                         if (diag != NULL && req_hwgrp_flag)
 427                                 *diag = VNIC_IOC_DIAG_NO_HWRINGS;
 428                         goto bail;
 429                 }
 430 
 431                 /* register to receive notification from underlying MAC */
 432                 vnic->vn_mnh = mac_notify_add(vnic->vn_lower_mh, vnic_notify_cb,
 433                     vnic);
 434 
 435                 *vnic_addr_type = vnic->vn_addr_type;
 436                 vnic->vn_addr_len = *mac_len;
 437                 vnic->vn_vid = vid;
 438 
 439                 bcopy(mac_addr, vnic->vn_addr, vnic->vn_addr_len);
 440 
 441                 if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY)
 442                         vnic->vn_slot_id = *mac_slot;
 443 
 444                 /*
 445                  * Set the initial VNIC capabilities. If the VNIC is created
 446                  * over MACs which does not support nactive vlan, disable
 447                  * VNIC's hardware checksum capability if its VID is not 0,
 448                  * since the underlying MAC would get the hardware checksum
 449                  * offset wrong in case of VLAN packets.
 450                  */
 451                 if (vid == 0 || !mac_capab_get(vnic->vn_lower_mh,
 452                     MAC_CAPAB_NO_NATIVEVLAN, NULL)) {
 453                         if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_HCKSUM,
 454                             &vnic->vn_hcksum_txflags))
 455                                 vnic->vn_hcksum_txflags = 0;
 456                 } else {
 457                         vnic->vn_hcksum_txflags = 0;
 458                 }
 459         }
 460 
 461         /* register with the MAC module */
 462         if ((mac = mac_alloc(MAC_VERSION)) == NULL)
 463                 goto bail;
 464 
 465         mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
 466         mac->m_driver = vnic;
 467         mac->m_dip = vnic_get_dip();
 468         mac->m_instance = (uint_t)-1;
 469         mac->m_src_addr = vnic->vn_addr;
 470         mac->m_callbacks = &vnic_m_callbacks;
 471 
 472         if (!is_anchor) {
 473                 /*
 474                  * If this is a VNIC based VLAN, then we check for the
 475                  * margin unless it has been created with the force
 476                  * flag. If we are configuring a VLAN over an etherstub,
 477                  * we don't check the margin even if force is not set.
 478                  */
 479                 if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) {
 480                         if (vid != VLAN_ID_NONE)
 481                                 vnic->vn_force = B_TRUE;
 482                         /*
 483                          * As the current margin size of the underlying mac is
 484                          * used to determine the margin size of the VNIC
 485                          * itself, request the underlying mac not to change
 486                          * to a smaller margin size.
 487                          */
 488                         err = mac_margin_add(vnic->vn_lower_mh,
 489                             &vnic->vn_margin, B_TRUE);
 490                         ASSERT(err == 0);
 491                 } else {
 492                         vnic->vn_margin = VLAN_TAGSZ;
 493                         err = mac_margin_add(vnic->vn_lower_mh,
 494                             &vnic->vn_margin, B_FALSE);
 495                         if (err != 0) {
 496                                 mac_free(mac);
 497                                 if (diag != NULL)
 498                                         *diag = VNIC_IOC_DIAG_MACMARGIN_INVALID;
 499                                 goto bail;
 500                         }
 501                 }
 502 
 503                 mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu,
 504                     &mac->m_max_sdu);
 505                 err = mac_mtu_add(vnic->vn_lower_mh, &mac->m_max_sdu, B_FALSE);
 506                 if (err != 0) {
 507                         VERIFY(mac_margin_remove(vnic->vn_lower_mh,
 508                             vnic->vn_margin) == 0);
 509                         mac_free(mac);
 510                         if (diag != NULL)
 511                                 *diag = VNIC_IOC_DIAG_MACMTU_INVALID;
 512                         goto bail;
 513                 }
 514                 vnic->vn_mtu = mac->m_max_sdu;
 515         } else {
 516                 vnic->vn_margin = VLAN_TAGSZ;
 517                 mac->m_min_sdu = 1;
 518                 mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU;
 519                 vnic->vn_mtu = ANCHOR_VNIC_MAX_MTU;
 520         }
 521 
 522         mac->m_margin = vnic->vn_margin;
 523 
 524         err = mac_register(mac, &vnic->vn_mh);
 525         mac_free(mac);
 526         if (err != 0) {
 527                 if (!is_anchor) {
 528                         VERIFY(mac_mtu_remove(vnic->vn_lower_mh,
 529                             vnic->vn_mtu) == 0);
 530                         VERIFY(mac_margin_remove(vnic->vn_lower_mh,
 531                             vnic->vn_margin) == 0);
 532                 }
 533                 goto bail;
 534         }
 535 
 536         /* Set the VNIC's MAC in the client */
 537         if (!is_anchor) {
 538                 mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh, mrp);
 539 
 540                 if (mrp != NULL) {
 541                         if ((mrp->mrp_mask & MRP_RX_RINGS) != 0 ||
 542                             (mrp->mrp_mask & MRP_TX_RINGS) != 0) {
 543                                 req_hwgrp_flag = B_TRUE;
 544                         }
 545                         err = mac_client_set_resources(vnic->vn_mch, mrp);
 546                         if (err != 0) {
 547                                 VERIFY(mac_mtu_remove(vnic->vn_lower_mh,
 548                                     vnic->vn_mtu) == 0);
 549                                 VERIFY(mac_margin_remove(vnic->vn_lower_mh,
 550                                     vnic->vn_margin) == 0);
 551                                 (void) mac_unregister(vnic->vn_mh);
 552                                 goto bail;
 553                         }
 554                 }
 555         }
 556 
 557         err = dls_devnet_create(vnic->vn_mh, vnic->vn_id, crgetzoneid(credp));
 558         if (err != 0) {
 559                 VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
 560                     vnic->vn_margin) == 0);
 561                 if (!is_anchor) {
 562                         VERIFY(mac_mtu_remove(vnic->vn_lower_mh,
 563                             vnic->vn_mtu) == 0);
 564                         VERIFY(mac_margin_remove(vnic->vn_lower_mh,
 565                             vnic->vn_margin) == 0);
 566                 }
 567                 (void) mac_unregister(vnic->vn_mh);
 568                 goto bail;
 569         }
 570 
 571         /* add new VNIC to hash table */
 572         err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id),
 573             (mod_hash_val_t)vnic);
 574         ASSERT(err == 0);
 575         vnic_count++;
 576 
 577         /*
 578          * Now that we've enabled this VNIC, we should go through and update the
 579          * link state by setting it to our parents.
 580          */
 581         vnic->vn_enabled = B_TRUE;
 582 
 583         if (is_anchor) {
 584                 vnic->vn_ls = LINK_STATE_UP;
 585         } else {
 586                 vnic->vn_ls = mac_client_stat_get(vnic->vn_mch,
 587                     MAC_STAT_LINK_STATE);
 588         }
 589         mac_link_update(vnic->vn_mh, vnic->vn_ls);
 590 
 591         rw_exit(&vnic_lock);
 592 
 593         return (0);
 594 
 595 bail:
 596         rw_exit(&vnic_lock);
 597         if (!is_anchor) {
 598                 if (vnic->vn_mnh != NULL)
 599                         (void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
 600                 if (vnic->vn_muh != NULL)
 601                         (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
 602                 if (vnic->vn_mch != NULL)
 603                         mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
 604                 if (vnic->vn_lower_mh != NULL)
 605                         mac_close(vnic->vn_lower_mh);
 606         }
 607 
 608         kmem_cache_free(vnic_cache, vnic);
 609         return (err);
 610 }
 611 
 612 /*
 613  * Modify the properties of an existing VNIC.
 614  */
 615 /* ARGSUSED */
 616 int
 617 vnic_dev_modify(datalink_id_t vnic_id, uint_t modify_mask,
 618     vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr,
 619     uint_t mac_slot, mac_resource_props_t *mrp)
 620 {
 621         vnic_t *vnic = NULL;
 622 
 623         rw_enter(&vnic_lock, RW_WRITER);
 624 
 625         if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
 626             (mod_hash_val_t *)&vnic) != 0) {
 627                 rw_exit(&vnic_lock);
 628                 return (ENOENT);
 629         }
 630 
 631         rw_exit(&vnic_lock);
 632 
 633         return (0);
 634 }
 635 
 636 /* ARGSUSED */
 637 int
 638 vnic_dev_delete(datalink_id_t vnic_id, uint32_t flags, cred_t *credp)
 639 {
 640         vnic_t *vnic = NULL;
 641         mod_hash_val_t val;
 642         datalink_id_t tmpid;
 643         int rc;
 644 
 645         rw_enter(&vnic_lock, RW_WRITER);
 646 
 647         if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
 648             (mod_hash_val_t *)&vnic) != 0) {
 649                 rw_exit(&vnic_lock);
 650                 return (ENOENT);
 651         }
 652 
 653         if ((rc = dls_devnet_destroy(vnic->vn_mh, &tmpid, B_TRUE)) != 0) {
 654                 rw_exit(&vnic_lock);
 655                 return (rc);
 656         }
 657 
 658         ASSERT(vnic_id == tmpid);
 659 
 660         /*
 661          * We cannot unregister the MAC yet. Unregistering would
 662          * free up mac_impl_t which should not happen at this time.
 663          * So disable mac_impl_t by calling mac_disable(). This will prevent
 664          * any new claims on mac_impl_t.
 665          */
 666         if ((rc = mac_disable(vnic->vn_mh)) != 0) {
 667                 (void) dls_devnet_create(vnic->vn_mh, vnic_id,
 668                     crgetzoneid(credp));
 669                 rw_exit(&vnic_lock);
 670                 return (rc);
 671         }
 672 
 673         vnic_cleanup_secondary_macs(vnic, vnic->vn_nhandles);
 674 
 675         vnic->vn_enabled = B_FALSE;
 676         (void) mod_hash_remove(vnic_hash, VNIC_HASH_KEY(vnic_id), &val);
 677         ASSERT(vnic == (vnic_t *)val);
 678         vnic_count--;
 679         rw_exit(&vnic_lock);
 680 
 681         /*
 682          * XXX-nicolas shouldn't have a void cast here, if it's
 683          * expected that the function will never fail, then we should
 684          * have an ASSERT().
 685          */
 686         (void) mac_unregister(vnic->vn_mh);
 687 
 688         if (vnic->vn_lower_mh != NULL) {
 689                 /*
 690                  * Check if MAC address for the vnic was obtained from the
 691                  * factory MAC addresses. If yes, release it.
 692                  */
 693                 if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
 694                         (void) mac_addr_factory_release(vnic->vn_mch,
 695                             vnic->vn_slot_id);
 696                 }
 697                 (void) mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin);
 698                 (void) mac_mtu_remove(vnic->vn_lower_mh, vnic->vn_mtu);
 699                 (void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
 700                 (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
 701                 mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
 702                 mac_close(vnic->vn_lower_mh);
 703         }
 704 
 705         kmem_cache_free(vnic_cache, vnic);
 706         return (0);
 707 }
 708 
 709 /* ARGSUSED */
 710 mblk_t *
 711 vnic_m_tx(void *arg, mblk_t *mp_chain)
 712 {
 713         /*
 714          * This function could be invoked for an anchor VNIC when sending
 715          * broadcast and multicast packets, and unicast packets which did
 716          * not match any local known destination.
 717          */
 718         freemsgchain(mp_chain);
 719         return (NULL);
 720 }
 721 
 722 /*ARGSUSED*/
 723 static void
 724 vnic_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
 725 {
 726         miocnak(q, mp, 0, ENOTSUP);
 727 }
 728 
 729 /*
 730  * This entry point cannot be passed-through, since it is invoked
 731  * for the per-VNIC kstats which must be exported independently
 732  * of the existence of VNIC MAC clients.
 733  */
 734 static int
 735 vnic_m_stat(void *arg, uint_t stat, uint64_t *val)
 736 {
 737         vnic_t *vnic = arg;
 738         int rval = 0;
 739 
 740         if (vnic->vn_lower_mh == NULL) {
 741                 /*
 742                  * It's an anchor VNIC, which does not have any
 743                  * statistics in itself.
 744                  */
 745                 return (ENOTSUP);
 746         }
 747 
 748         /*
 749          * ENOTSUP must be reported for unsupported stats, the VNIC
 750          * driver reports a subset of the stats that would
 751          * be returned by a real piece of hardware.
 752          */
 753 
 754         switch (stat) {
 755         case MAC_STAT_LINK_STATE:
 756         case MAC_STAT_LINK_UP:
 757         case MAC_STAT_PROMISC:
 758         case MAC_STAT_IFSPEED:
 759         case MAC_STAT_MULTIRCV:
 760         case MAC_STAT_MULTIXMT:
 761         case MAC_STAT_BRDCSTRCV:
 762         case MAC_STAT_BRDCSTXMT:
 763         case MAC_STAT_OPACKETS:
 764         case MAC_STAT_OBYTES:
 765         case MAC_STAT_IERRORS:
 766         case MAC_STAT_OERRORS:
 767         case MAC_STAT_RBYTES:
 768         case MAC_STAT_IPACKETS:
 769                 *val = mac_client_stat_get(vnic->vn_mch, stat);
 770                 break;
 771         default:
 772                 rval = ENOTSUP;
 773         }
 774 
 775         return (rval);
 776 }
 777 
 778 /*
 779  * Invoked by the upper MAC to retrieve the lower MAC client handle
 780  * corresponding to a VNIC. A pointer to this function is obtained
 781  * by the upper MAC via capability query.
 782  *
 783  * XXX-nicolas Note: this currently causes all VNIC MAC clients to
 784  * receive the same MAC client handle for the same VNIC. This is ok
 785  * as long as we have only one VNIC MAC client which sends and
 786  * receives data, but we don't currently enforce this at the MAC layer.
 787  */
 788 static void *
 789 vnic_mac_client_handle(void *vnic_arg)
 790 {
 791         vnic_t *vnic = vnic_arg;
 792 
 793         return (vnic->vn_mch);
 794 }
 795 
 796 /*
 797  * Invoked when updating the primary MAC so that the secondary MACs are
 798  * kept in sync.
 799  */
 800 static void
 801 vnic_mac_secondary_update(void *vnic_arg)
 802 {
 803         vnic_t *vn = vnic_arg;
 804         int i;
 805 
 806         for (i = 1; i <= vn->vn_nhandles; i++) {
 807                 mac_secondary_dup(vn->vn_mc_handles[0], vn->vn_mc_handles[i]);
 808         }
 809 }
 810 
 811 /*
 812  * Return information about the specified capability.
 813  */
 814 /* ARGSUSED */
 815 static boolean_t
 816 vnic_m_capab_get(void *arg, mac_capab_t cap, void *cap_data)
 817 {
 818         vnic_t *vnic = arg;
 819 
 820         switch (cap) {
 821         case MAC_CAPAB_HCKSUM: {
 822                 uint32_t *hcksum_txflags = cap_data;
 823 
 824                 *hcksum_txflags = vnic->vn_hcksum_txflags &
 825                     (HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM |
 826                     HCKSUM_INET_PARTIAL);
 827                 break;
 828         }
 829         case MAC_CAPAB_VNIC: {
 830                 mac_capab_vnic_t *vnic_capab = cap_data;
 831 
 832                 if (vnic->vn_lower_mh == NULL) {
 833                         /*
 834                          * It's an anchor VNIC, we don't have an underlying
 835                          * NIC and MAC client handle.
 836                          */
 837                         return (B_FALSE);
 838                 }
 839 
 840                 if (vnic_capab != NULL) {
 841                         vnic_capab->mcv_arg = vnic;
 842                         vnic_capab->mcv_mac_client_handle =
 843                             vnic_mac_client_handle;
 844                         vnic_capab->mcv_mac_secondary_update =
 845                             vnic_mac_secondary_update;
 846                 }
 847                 break;
 848         }
 849         case MAC_CAPAB_ANCHOR_VNIC: {
 850                 /* since it's an anchor VNIC we don't have lower mac handle */
 851                 if (vnic->vn_lower_mh == NULL) {
 852                         ASSERT(vnic->vn_link_id == 0);
 853                         return (B_TRUE);
 854                 }
 855                 return (B_FALSE);
 856         }
 857         case MAC_CAPAB_NO_NATIVEVLAN:
 858                 return (B_FALSE);
 859         case MAC_CAPAB_NO_ZCOPY:
 860                 return (B_TRUE);
 861         case MAC_CAPAB_VRRP: {
 862                 mac_capab_vrrp_t *vrrp_capab = cap_data;
 863 
 864                 if (vnic->vn_vrid != 0) {
 865                         if (vrrp_capab != NULL)
 866                                 vrrp_capab->mcv_af = vnic->vn_af;
 867                         return (B_TRUE);
 868                 }
 869                 return (B_FALSE);
 870         }
 871         default:
 872                 return (B_FALSE);
 873         }
 874         return (B_TRUE);
 875 }
 876 
 877 /* ARGSUSED */
 878 static int
 879 vnic_m_start(void *arg)
 880 {
 881         return (0);
 882 }
 883 
 884 /* ARGSUSED */
 885 static void
 886 vnic_m_stop(void *arg)
 887 {
 888 }
 889 
 890 /* ARGSUSED */
 891 static int
 892 vnic_m_promisc(void *arg, boolean_t on)
 893 {
 894         return (0);
 895 }
 896 
 897 /* ARGSUSED */
 898 static int
 899 vnic_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
 900 {
 901         return (0);
 902 }
 903 
 904 static int
 905 vnic_m_unicst(void *arg, const uint8_t *macaddr)
 906 {
 907         vnic_t *vnic = arg;
 908 
 909         return (mac_vnic_unicast_set(vnic->vn_mch, macaddr));
 910 }
 911 
 912 static void
 913 vnic_cleanup_secondary_macs(vnic_t *vn, int cnt)
 914 {
 915         int i;
 916 
 917         /* Remove existing secondaries (primary is at 0) */
 918         for (i = 1; i <= cnt; i++) {
 919                 mac_rx_clear(vn->vn_mc_handles[i]);
 920 
 921                 /* unicast handle might not have been set yet */
 922                 if (vn->vn_mu_handles[i] != NULL)
 923                         (void) mac_unicast_remove(vn->vn_mc_handles[i],
 924                             vn->vn_mu_handles[i]);
 925 
 926                 mac_secondary_cleanup(vn->vn_mc_handles[i]);
 927 
 928                 mac_client_close(vn->vn_mc_handles[i], MAC_CLOSE_FLAGS_IS_VNIC);
 929 
 930                 vn->vn_mu_handles[i] = NULL;
 931                 vn->vn_mc_handles[i] = NULL;
 932         }
 933 
 934         vn->vn_nhandles = 0;
 935 }
 936 
 937 /*
 938  * Setup secondary MAC addresses on the vnic. Due to limitations in the mac
 939  * code, each mac address must be associated with a mac_client (and the
 940  * flow that goes along with the client) so we need to create those clients
 941  * here.
 942  */
 943 static int
 944 vnic_set_secondary_macs(vnic_t *vn, mac_secondary_addr_t *msa)
 945 {
 946         int i, err;
 947         char primary_name[MAXNAMELEN];
 948 
 949         /* First, remove pre-existing secondaries */
 950         ASSERT(vn->vn_nhandles < MPT_MAXMACADDR);
 951         vnic_cleanup_secondary_macs(vn, vn->vn_nhandles);
 952 
 953         if (msa->ms_addrcnt == (uint32_t)-1)
 954                 msa->ms_addrcnt = 0;
 955 
 956         vn->vn_nhandles = msa->ms_addrcnt;
 957 
 958         (void) dls_mgmt_get_linkinfo(vn->vn_id, primary_name, NULL, NULL, NULL);
 959 
 960         /*
 961          * Now add the new secondary MACs
 962          * Recall that the primary MAC address is the first element.
 963          * The secondary clients are named after the primary with their
 964          * index to distinguish them.
 965          */
 966         for (i = 1; i <= vn->vn_nhandles; i++) {
 967                 uint8_t *addr;
 968                 mac_diag_t mac_diag;
 969                 char secondary_name[MAXNAMELEN];
 970 
 971                 (void) snprintf(secondary_name, sizeof (secondary_name),
 972                     "%s%02d", primary_name, i);
 973 
 974                 err = mac_client_open(vn->vn_lower_mh, &vn->vn_mc_handles[i],
 975                     secondary_name, MAC_OPEN_FLAGS_IS_VNIC);
 976                 if (err != 0) {
 977                         /* Remove any that we successfully added */
 978                         vnic_cleanup_secondary_macs(vn, --i);
 979                         return (err);
 980                 }
 981 
 982                 /*
 983                  * Assign a MAC address to the VNIC
 984                  *
 985                  * Normally this would be done with vnic_unicast_add but since
 986                  * we know these are fixed adddresses, and since we need to
 987                  * save this in the proper array slot, we bypass that function
 988                  * and go direct.
 989                  */
 990                 addr = msa->ms_addrs[i - 1];
 991                 err = mac_unicast_add(vn->vn_mc_handles[i], addr, 0,
 992                     &vn->vn_mu_handles[i], vn->vn_vid, &mac_diag);
 993                 if (err != 0) {
 994                         /* Remove any that we successfully added */
 995                         vnic_cleanup_secondary_macs(vn, i);
 996                         return (err);
 997                 }
 998 
 999                 /*
1000                  * Setup the secondary the same way as the primary (i.e.
1001                  * receiver function/argument (e.g. i_dls_link_rx, mac_pkt_drop,
1002                  * etc.), the promisc list, and the resource controls).
1003                  */
1004                 mac_secondary_dup(vn->vn_mc_handles[0], vn->vn_mc_handles[i]);
1005         }
1006 
1007         return (0);
1008 }
1009 
1010 static int
1011 vnic_get_secondary_macs(vnic_t *vn, uint_t pr_valsize, void *pr_val)
1012 {
1013         int i;
1014         mac_secondary_addr_t msa;
1015 
1016         if (pr_valsize < sizeof (msa))
1017                 return (EINVAL);
1018 
1019         /* Get existing addresses (primary is at 0) */
1020         ASSERT(vn->vn_nhandles < MPT_MAXMACADDR);
1021         for (i = 1; i <= vn->vn_nhandles; i++) {
1022                 ASSERT(vn->vn_mc_handles[i] != NULL);
1023                 mac_unicast_secondary_get(vn->vn_mc_handles[i],
1024                     msa.ms_addrs[i - 1]);
1025         }
1026         msa.ms_addrcnt = vn->vn_nhandles;
1027 
1028         bcopy(&msa, pr_val, sizeof (msa));
1029         return (0);
1030 }
1031 
1032 /*
1033  * Callback functions for set/get of properties
1034  */
1035 /*ARGSUSED*/
1036 static int
1037 vnic_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
1038     uint_t pr_valsize, const void *pr_val)
1039 {
1040         int             err = 0;
1041         vnic_t          *vn = m_driver;
1042 
1043         switch (pr_num) {
1044         case MAC_PROP_MTU: {
1045                 uint32_t        mtu;
1046 
1047                 if (pr_valsize < sizeof (mtu)) {
1048                         err = EINVAL;
1049                         break;
1050                 }
1051                 bcopy(pr_val, &mtu, sizeof (mtu));
1052 
1053                 if (vn->vn_link_id == DATALINK_INVALID_LINKID) {
1054                         if (mtu < ANCHOR_VNIC_MIN_MTU ||
1055                             mtu > ANCHOR_VNIC_MAX_MTU) {
1056                                 err = EINVAL;
1057                                 break;
1058                         }
1059                 } else {
1060                         err = mac_mtu_add(vn->vn_lower_mh, &mtu, B_FALSE);
1061                         /*
1062                          * If it's not supported to set a value here, translate
1063                          * that to EINVAL, so user land gets a better idea of
1064                          * what went wrong. This realistically means that they
1065                          * violated the output of prop info.
1066                          */
1067                         if (err == ENOTSUP)
1068                                 err = EINVAL;
1069                         if (err != 0)
1070                                 break;
1071                         VERIFY(mac_mtu_remove(vn->vn_lower_mh,
1072                             vn->vn_mtu) == 0);
1073                 }
1074                 vn->vn_mtu = mtu;
1075                 err = mac_maxsdu_update(vn->vn_mh, mtu);
1076                 break;
1077         }
1078         case MAC_PROP_VN_PROMISC_FILTERED: {
1079                 boolean_t filtered;
1080 
1081                 if (pr_valsize < sizeof (filtered)) {
1082                         err = EINVAL;
1083                         break;
1084                 }
1085 
1086                 bcopy(pr_val, &filtered, sizeof (filtered));
1087                 mac_set_promisc_filtered(vn->vn_mch, filtered);
1088                 break;
1089         }
1090         case MAC_PROP_SECONDARY_ADDRS: {
1091                 mac_secondary_addr_t msa;
1092 
1093                 bcopy(pr_val, &msa, sizeof (msa));
1094                 err = vnic_set_secondary_macs(vn, &msa);
1095                 break;
1096         }
1097         case MAC_PROP_PRIVATE: {
1098                 long val, i;
1099                 const char *v;
1100 
1101                 if (vn->vn_link_id != DATALINK_INVALID_LINKID ||
1102                     strcmp(pr_name, "_linkstate") != 0) {
1103                         err = ENOTSUP;
1104                         break;
1105                 }
1106 
1107                 for (v = pr_val, i = 0; i < pr_valsize; i++, v++) {
1108                         if (*v == '\0')
1109                                 break;
1110                 }
1111                 if (i == pr_valsize) {
1112                         err = EINVAL;
1113                         break;
1114                 }
1115 
1116                 (void) ddi_strtol(pr_val, (char **)NULL, 0, &val);
1117                 if (val != LINK_STATE_UP && val != LINK_STATE_DOWN) {
1118                         err = EINVAL;
1119                         break;
1120                 }
1121                 vn->vn_ls = val;
1122                 mac_link_update(vn->vn_mh, vn->vn_ls);
1123                 break;
1124         }
1125         default:
1126                 err = ENOTSUP;
1127                 break;
1128         }
1129         return (err);
1130 }
1131 
1132 /* ARGSUSED */
1133 static int
1134 vnic_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1135     uint_t pr_valsize, void *pr_val)
1136 {
1137         vnic_t          *vn = arg;
1138         int             ret = 0;
1139         boolean_t       out;
1140 
1141         switch (pr_num) {
1142         case MAC_PROP_VN_PROMISC_FILTERED:
1143                 out = mac_get_promisc_filtered(vn->vn_mch);
1144                 ASSERT(pr_valsize >= sizeof (boolean_t));
1145                 bcopy(&out, pr_val, sizeof (boolean_t));
1146                 break;
1147         case MAC_PROP_SECONDARY_ADDRS:
1148                 ret = vnic_get_secondary_macs(vn, pr_valsize, pr_val);
1149                 break;
1150         case MAC_PROP_PRIVATE:
1151                 if (vn->vn_link_id != DATALINK_INVALID_LINKID) {
1152                         ret = EINVAL;
1153                         break;
1154                 }
1155 
1156                 if (strcmp(pr_name, "_linkstate") != 0) {
1157                         ret = EINVAL;
1158                         break;
1159                 }
1160                 (void) snprintf(pr_val, pr_valsize, "%d", vn->vn_ls);
1161                 break;
1162         default:
1163                 ret = ENOTSUP;
1164                 break;
1165         }
1166 
1167         return (ret);
1168 }
1169 
1170 /* ARGSUSED */
1171 static void
1172 vnic_m_propinfo(void *m_driver, const char *pr_name,
1173     mac_prop_id_t pr_num, mac_prop_info_handle_t prh)
1174 {
1175         vnic_t          *vn = m_driver;
1176 
1177         switch (pr_num) {
1178         case MAC_PROP_MTU:
1179                 if (vn->vn_link_id == DATALINK_INVALID_LINKID) {
1180                         mac_prop_info_set_range_uint32(prh,
1181                             ANCHOR_VNIC_MIN_MTU, ANCHOR_VNIC_MAX_MTU);
1182                 } else {
1183                         uint32_t                max;
1184                         mac_perim_handle_t      mph;
1185                         mac_propval_range_t     range;
1186 
1187                         /*
1188                          * The valid range for a VNIC's MTU is the minimum that
1189                          * the device supports and the current value of the
1190                          * device. A VNIC cannot increase the current MTU of the
1191                          * device. Therefore we need to get the range from the
1192                          * propinfo endpoint and current mtu from the
1193                          * traditional property endpoint.
1194                          */
1195                         mac_perim_enter_by_mh(vn->vn_lower_mh, &mph);
1196                         if (mac_get_prop(vn->vn_lower_mh, MAC_PROP_MTU, "mtu",
1197                             &max, sizeof (uint32_t)) != 0) {
1198                                 mac_perim_exit(mph);
1199                                 return;
1200                         }
1201 
1202                         range.mpr_count = 1;
1203                         if (mac_prop_info(vn->vn_lower_mh, MAC_PROP_MTU, "mtu",
1204                             NULL, 0, &range, NULL) != 0) {
1205                                 mac_perim_exit(mph);
1206                                 return;
1207                         }
1208 
1209                         mac_prop_info_set_default_uint32(prh, max);
1210                         mac_prop_info_set_range_uint32(prh,
1211                             range.mpr_range_uint32[0].mpur_min, max);
1212                         mac_perim_exit(mph);
1213                 }
1214                 break;
1215         case MAC_PROP_PRIVATE:
1216                 if (vn->vn_link_id != DATALINK_INVALID_LINKID)
1217                         break;
1218 
1219                 if (strcmp(pr_name, "_linkstate") == 0) {
1220                         char buf[16];
1221 
1222                         mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
1223                         (void) snprintf(buf, sizeof (buf), "%d", vn->vn_ls);
1224                         mac_prop_info_set_default_str(prh, buf);
1225                 }
1226                 break;
1227         }
1228 }
1229 
1230 
1231 int
1232 vnic_info(vnic_info_t *info, cred_t *credp)
1233 {
1234         vnic_t          *vnic;
1235         int             err;
1236 
1237         /* Make sure that the VNIC link is visible from the caller's zone. */
1238         if (!dls_devnet_islinkvisible(info->vn_vnic_id, crgetzoneid(credp)))
1239                 return (ENOENT);
1240 
1241         rw_enter(&vnic_lock, RW_WRITER);
1242 
1243         err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(info->vn_vnic_id),
1244             (mod_hash_val_t *)&vnic);
1245         if (err != 0) {
1246                 rw_exit(&vnic_lock);
1247                 return (ENOENT);
1248         }
1249 
1250         info->vn_link_id = vnic->vn_link_id;
1251         info->vn_mac_addr_type = vnic->vn_addr_type;
1252         info->vn_mac_len = vnic->vn_addr_len;
1253         bcopy(vnic->vn_addr, info->vn_mac_addr, MAXMACADDRLEN);
1254         info->vn_mac_slot = vnic->vn_slot_id;
1255         info->vn_mac_prefix_len = 0;
1256         info->vn_vid = vnic->vn_vid;
1257         info->vn_force = vnic->vn_force;
1258         info->vn_vrid = vnic->vn_vrid;
1259         info->vn_af = vnic->vn_af;
1260 
1261         bzero(&info->vn_resource_props, sizeof (mac_resource_props_t));
1262         if (vnic->vn_mch != NULL)
1263                 mac_client_get_resources(vnic->vn_mch,
1264                     &info->vn_resource_props);
1265 
1266         rw_exit(&vnic_lock);
1267         return (0);
1268 }
1269 
1270 static void
1271 vnic_notify_cb(void *arg, mac_notify_type_t type)
1272 {
1273         vnic_t *vnic = arg;
1274 
1275         /*
1276          * Do not deliver notifications if the vnic is not fully initialized
1277          * or is in process of being torn down.
1278          */
1279         if (!vnic->vn_enabled)
1280                 return;
1281 
1282         switch (type) {
1283         case MAC_NOTE_UNICST:
1284                 /*
1285                  * Only the VLAN VNIC needs to be notified with primary MAC
1286                  * address change.
1287                  */
1288                 if (vnic->vn_addr_type != VNIC_MAC_ADDR_TYPE_PRIMARY)
1289                         return;
1290 
1291                 /*  the unicast MAC address value */
1292                 mac_unicast_primary_get(vnic->vn_lower_mh, vnic->vn_addr);
1293 
1294                 /* notify its upper layer MAC about MAC address change */
1295                 mac_unicst_update(vnic->vn_mh, (const uint8_t *)vnic->vn_addr);
1296                 break;
1297 
1298         case MAC_NOTE_LINK:
1299                 vnic->vn_ls = mac_client_stat_get(vnic->vn_mch,
1300                     MAC_STAT_LINK_STATE);
1301                 mac_link_update(vnic->vn_mh, vnic->vn_ls);
1302                 break;
1303 
1304         default:
1305                 break;
1306         }
1307 }