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 2015 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 } 1308