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