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
84static int vnic_m_start(void *);
85static void vnic_m_stop(void *);
86static int vnic_m_promisc(void *, boolean_t);
87static int vnic_m_multicst(void *, boolean_t, const uint8_t *);
88static int vnic_m_unicst(void *, const uint8_t *);
89static int vnic_m_stat(void *, uint_t, uint64_t *);
90static void vnic_m_ioctl(void *, queue_t *, mblk_t *);
91static int vnic_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
92    const void *);
93static int vnic_m_getprop(void *, const char *, mac_prop_id_t, uint_t, void *);
94static void vnic_m_propinfo(void *, const char *, mac_prop_id_t,
95    mac_prop_info_handle_t);
96static mblk_t *vnic_m_tx(void *, mblk_t *);
97static boolean_t vnic_m_capab_get(void *, mac_capab_t, void *);
98static void vnic_notify_cb(void *, mac_notify_type_t);
99static void vnic_cleanup_secondary_macs(vnic_t *, int);
100
101static kmem_cache_t	*vnic_cache;
102static krwlock_t	vnic_lock;
103static 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 */
109static 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
116static 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
135void
136vnic_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
149void
150vnic_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
159uint_t
160vnic_dev_count(void)
161{
162	return (vnic_count);
163}
164
165static vnic_ioc_diag_t
166vnic_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
196static int
197vnic_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 */
339int
340vnic_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
610bail:
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 */
631int
632vnic_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 */
652int
653vnic_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 */
725mblk_t *
726vnic_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*/
738static void
739vnic_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 */
749static int
750vnic_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 */
803static void *
804vnic_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 */
815static void
816vnic_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 */
830static boolean_t
831vnic_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 */
902static int
903vnic_m_start(void *arg)
904{
905	return (0);
906}
907
908/* ARGSUSED */
909static void
910vnic_m_stop(void *arg)
911{
912}
913
914/* ARGSUSED */
915static int
916vnic_m_promisc(void *arg, boolean_t on)
917{
918	return (0);
919}
920
921/* ARGSUSED */
922static int
923vnic_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
924{
925	return (0);
926}
927
928static int
929vnic_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
936static void
937vnic_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 */
967static int
968vnic_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
1034static int
1035vnic_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*/
1060static int
1061vnic_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 */
1157static int
1158vnic_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 */
1195static void
1196vnic_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
1255int
1256vnic_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
1294static void
1295vnic_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