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