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