xref: /illumos-gate/usr/src/uts/common/io/vnic/vnic_dev.c (revision 84de666edc7f7d835057ae4807a387447c086bcf)
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 }
1308