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 (c) 2012, Joyent, Inc. All rights reserved.
24 */
25
26 #include <sys/types.h>
27 #include <sys/cred.h>
28 #include <sys/sysmacros.h>
29 #include <sys/conf.h>
30 #include <sys/cmn_err.h>
31 #include <sys/list.h>
32 #include <sys/ksynch.h>
33 #include <sys/kmem.h>
34 #include <sys/stream.h>
35 #include <sys/modctl.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/atomic.h>
39 #include <sys/stat.h>
40 #include <sys/modhash.h>
41 #include <sys/strsubr.h>
42 #include <sys/strsun.h>
43 #include <sys/dlpi.h>
44 #include <sys/mac.h>
45 #include <sys/mac_provider.h>
46 #include <sys/mac_client.h>
47 #include <sys/mac_client_priv.h>
48 #include <sys/mac_ether.h>
49 #include <sys/dls.h>
50 #include <sys/pattr.h>
51 #include <sys/time.h>
52 #include <sys/vlan.h>
53 #include <sys/vnic.h>
54 #include <sys/vnic_impl.h>
55 #include <sys/mac_impl.h>
56 #include <sys/mac_flow_impl.h>
57 #include <inet/ip_impl.h>
58
59 /*
60 * Note that for best performance, the VNIC is a passthrough design.
61 * For each VNIC corresponds a MAC client of the underlying MAC (lower MAC).
62 * This MAC client is opened by the VNIC driver at VNIC creation,
63 * and closed when the VNIC is deleted.
64 * When a MAC client of the VNIC itself opens a VNIC, the MAC layer
65 * (upper MAC) detects that the MAC being opened is a VNIC. Instead
66 * of allocating a new MAC client, it asks the VNIC driver to return
67 * the lower MAC client handle associated with the VNIC, and that handle
68 * is returned to the upper MAC client directly. This allows access
69 * by upper MAC clients of the VNIC to have direct access to the lower
70 * MAC client for the control path and data path.
71 *
72 * Due to this passthrough, some of the entry points exported by the
73 * VNIC driver are never directly invoked. These entry points include
74 * vnic_m_start, vnic_m_stop, vnic_m_promisc, vnic_m_multicst, etc.
75 */
76
77 static int vnic_m_start(void *);
78 static void vnic_m_stop(void *);
79 static int vnic_m_promisc(void *, boolean_t);
80 static int vnic_m_multicst(void *, boolean_t, const uint8_t *);
81 static int vnic_m_unicst(void *, const uint8_t *);
82 static int vnic_m_stat(void *, uint_t, uint64_t *);
83 static void vnic_m_ioctl(void *, queue_t *, mblk_t *);
84 static int vnic_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
85 const void *);
86 static int vnic_m_getprop(void *, const char *, mac_prop_id_t, uint_t, void *);
87 static void vnic_m_propinfo(void *, const char *, mac_prop_id_t,
88 mac_prop_info_handle_t);
89 static mblk_t *vnic_m_tx(void *, mblk_t *);
90 static boolean_t vnic_m_capab_get(void *, mac_capab_t, void *);
91 static void vnic_notify_cb(void *, mac_notify_type_t);
92
93 static kmem_cache_t *vnic_cache;
94 static krwlock_t vnic_lock;
95 static uint_t vnic_count;
96
97 #define ANCHOR_VNIC_MIN_MTU 576
98 #define ANCHOR_VNIC_MAX_MTU 9000
99
100 /* hash of VNICs (vnic_t's), keyed by VNIC id */
101 static mod_hash_t *vnic_hash;
102 #define VNIC_HASHSZ 64
103 #define VNIC_HASH_KEY(vnic_id) ((mod_hash_key_t)(uintptr_t)vnic_id)
104
105 #define VNIC_M_CALLBACK_FLAGS \
106 (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO)
107
108 static mac_callbacks_t vnic_m_callbacks = {
109 VNIC_M_CALLBACK_FLAGS,
110 vnic_m_stat,
111 vnic_m_start,
112 vnic_m_stop,
113 vnic_m_promisc,
114 vnic_m_multicst,
115 vnic_m_unicst,
116 vnic_m_tx,
117 NULL,
118 vnic_m_ioctl,
119 vnic_m_capab_get,
120 NULL,
121 NULL,
122 vnic_m_setprop,
123 vnic_m_getprop,
124 vnic_m_propinfo
125 };
126
127 void
128 vnic_dev_init(void)
129 {
130 vnic_cache = kmem_cache_create("vnic_cache",
131 sizeof (vnic_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
132
133 vnic_hash = mod_hash_create_idhash("vnic_hash",
134 VNIC_HASHSZ, mod_hash_null_valdtor);
135
136 rw_init(&vnic_lock, NULL, RW_DEFAULT, NULL);
137
138 vnic_count = 0;
139 }
140
141 void
142 vnic_dev_fini(void)
143 {
144 ASSERT(vnic_count == 0);
145
146 rw_destroy(&vnic_lock);
147 mod_hash_destroy_idhash(vnic_hash);
148 kmem_cache_destroy(vnic_cache);
149 }
150
151 uint_t
152 vnic_dev_count(void)
153 {
154 return (vnic_count);
155 }
156
157 static vnic_ioc_diag_t
158 vnic_mac2vnic_diag(mac_diag_t diag)
159 {
160 switch (diag) {
161 case MAC_DIAG_MACADDR_NIC:
162 return (VNIC_IOC_DIAG_MACADDR_NIC);
163 case MAC_DIAG_MACADDR_INUSE:
164 return (VNIC_IOC_DIAG_MACADDR_INUSE);
165 case MAC_DIAG_MACADDR_INVALID:
166 return (VNIC_IOC_DIAG_MACADDR_INVALID);
167 case MAC_DIAG_MACADDRLEN_INVALID:
168 return (VNIC_IOC_DIAG_MACADDRLEN_INVALID);
169 case MAC_DIAG_MACFACTORYSLOTINVALID:
170 return (VNIC_IOC_DIAG_MACFACTORYSLOTINVALID);
171 case MAC_DIAG_MACFACTORYSLOTUSED:
172 return (VNIC_IOC_DIAG_MACFACTORYSLOTUSED);
173 case MAC_DIAG_MACFACTORYSLOTALLUSED:
174 return (VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED);
175 case MAC_DIAG_MACFACTORYNOTSUP:
176 return (VNIC_IOC_DIAG_MACFACTORYNOTSUP);
177 case MAC_DIAG_MACPREFIX_INVALID:
178 return (VNIC_IOC_DIAG_MACPREFIX_INVALID);
179 case MAC_DIAG_MACPREFIXLEN_INVALID:
180 return (VNIC_IOC_DIAG_MACPREFIXLEN_INVALID);
181 case MAC_DIAG_MACNO_HWRINGS:
182 return (VNIC_IOC_DIAG_NO_HWRINGS);
183 default:
184 return (VNIC_IOC_DIAG_NONE);
185 }
186 }
187
188 static int
189 vnic_unicast_add(vnic_t *vnic, vnic_mac_addr_type_t vnic_addr_type,
190 int *addr_slot, uint_t prefix_len, int *addr_len_ptr_arg,
191 uint8_t *mac_addr_arg, uint16_t flags, vnic_ioc_diag_t *diag,
192 uint16_t vid, boolean_t req_hwgrp_flag)
193 {
194 mac_diag_t mac_diag;
195 uint16_t mac_flags = 0;
196 int err;
197 uint_t addr_len;
198
199 if (flags & VNIC_IOC_CREATE_NODUPCHECK)
200 mac_flags |= MAC_UNICAST_NODUPCHECK;
201
202 switch (vnic_addr_type) {
203 case VNIC_MAC_ADDR_TYPE_FIXED:
204 case VNIC_MAC_ADDR_TYPE_VRID:
205 /*
206 * The MAC address value to assign to the VNIC
207 * is already provided in mac_addr_arg. addr_len_ptr_arg
208 * already contains the MAC address length.
209 */
210 break;
211
212 case VNIC_MAC_ADDR_TYPE_RANDOM:
213 /*
214 * Random MAC address. There are two sub-cases:
215 *
216 * 1 - If mac_len == 0, a new MAC address is generated.
217 * The length of the MAC address to generated depends
218 * on the type of MAC used. The prefix to use for the MAC
219 * address is stored in the most significant bytes
220 * of the mac_addr argument, and its length is specified
221 * by the mac_prefix_len argument. This prefix can
222 * correspond to a IEEE OUI in the case of Ethernet,
223 * for example.
224 *
225 * 2 - If mac_len > 0, the address was already picked
226 * randomly, and is now passed back during VNIC
227 * re-creation. The mac_addr argument contains the MAC
228 * address that was generated. We distinguish this
229 * case from the fixed MAC address case, since we
230 * want the user consumers to know, when they query
231 * the list of VNICs, that a VNIC was assigned a
232 * random MAC address vs assigned a fixed address
233 * specified by the user.
234 */
235
236 /*
237 * If it's a pre-generated address, we're done. mac_addr_arg
238 * and addr_len_ptr_arg already contain the MAC address
239 * value and length.
240 */
241 if (*addr_len_ptr_arg > 0)
242 break;
243
244 /* generate a new random MAC address */
245 if ((err = mac_addr_random(vnic->vn_mch,
246 prefix_len, mac_addr_arg, &mac_diag)) != 0) {
247 *diag = vnic_mac2vnic_diag(mac_diag);
248 return (err);
249 }
250 *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
251 break;
252
253 case VNIC_MAC_ADDR_TYPE_FACTORY:
254 err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
255 if (err != 0) {
256 if (err == EINVAL)
257 *diag = VNIC_IOC_DIAG_MACFACTORYSLOTINVALID;
258 if (err == EBUSY)
259 *diag = VNIC_IOC_DIAG_MACFACTORYSLOTUSED;
260 if (err == ENOSPC)
261 *diag = VNIC_IOC_DIAG_MACFACTORYSLOTALLUSED;
262 return (err);
263 }
264
265 mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
266 mac_addr_arg, &addr_len, NULL, NULL);
267 *addr_len_ptr_arg = addr_len;
268 break;
269
270 case VNIC_MAC_ADDR_TYPE_AUTO:
271 /* first try to allocate a factory MAC address */
272 err = mac_addr_factory_reserve(vnic->vn_mch, addr_slot);
273 if (err == 0) {
274 mac_addr_factory_value(vnic->vn_lower_mh, *addr_slot,
275 mac_addr_arg, &addr_len, NULL, NULL);
276 vnic_addr_type = VNIC_MAC_ADDR_TYPE_FACTORY;
277 *addr_len_ptr_arg = addr_len;
278 break;
279 }
280
281 /*
282 * Allocating a factory MAC address failed, generate a
283 * random MAC address instead.
284 */
285 if ((err = mac_addr_random(vnic->vn_mch,
286 prefix_len, mac_addr_arg, &mac_diag)) != 0) {
287 *diag = vnic_mac2vnic_diag(mac_diag);
288 return (err);
289 }
290 *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
291 vnic_addr_type = VNIC_MAC_ADDR_TYPE_RANDOM;
292 break;
293 case VNIC_MAC_ADDR_TYPE_PRIMARY:
294 /*
295 * We get the address here since we copy it in the
296 * vnic's vn_addr.
297 * We can't ask for hardware resources since we
298 * don't currently support hardware classification
299 * for these MAC clients.
300 */
301 if (req_hwgrp_flag) {
302 *diag = VNIC_IOC_DIAG_NO_HWRINGS;
303 return (ENOTSUP);
304 }
305 mac_unicast_primary_get(vnic->vn_lower_mh, mac_addr_arg);
306 *addr_len_ptr_arg = mac_addr_len(vnic->vn_lower_mh);
307 mac_flags |= MAC_UNICAST_VNIC_PRIMARY;
308 break;
309 }
310
311 vnic->vn_addr_type = vnic_addr_type;
312
313 err = mac_unicast_add(vnic->vn_mch, mac_addr_arg, mac_flags,
314 &vnic->vn_muh, vid, &mac_diag);
315 if (err != 0) {
316 if (vnic_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
317 /* release factory MAC address */
318 mac_addr_factory_release(vnic->vn_mch, *addr_slot);
319 }
320 *diag = vnic_mac2vnic_diag(mac_diag);
321 }
322
323 return (err);
324 }
325
326 /*
327 * Create a new VNIC upon request from administrator.
328 * Returns 0 on success, an errno on failure.
329 */
330 /* ARGSUSED */
331 int
332 vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid,
333 vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr,
334 int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid,
335 int af, mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag,
336 cred_t *credp)
337 {
338 vnic_t *vnic;
339 mac_register_t *mac;
340 int err;
341 boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0);
342 char vnic_name[MAXNAMELEN];
343 const mac_info_t *minfop;
344 uint32_t req_hwgrp_flag = B_FALSE;
345
346 *diag = VNIC_IOC_DIAG_NONE;
347
348 rw_enter(&vnic_lock, RW_WRITER);
349
350 /* does a VNIC with the same id already exist? */
351 err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
352 (mod_hash_val_t *)&vnic);
353 if (err == 0) {
354 rw_exit(&vnic_lock);
355 return (EEXIST);
356 }
357
358 vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP);
359 if (vnic == NULL) {
360 rw_exit(&vnic_lock);
361 return (ENOMEM);
362 }
363
364 bzero(vnic, sizeof (*vnic));
365
366 vnic->vn_id = vnic_id;
367 vnic->vn_link_id = linkid;
368 vnic->vn_vrid = vrid;
369 vnic->vn_af = af;
370
371 if (!is_anchor) {
372 if (linkid == DATALINK_INVALID_LINKID) {
373 err = EINVAL;
374 goto bail;
375 }
376
377 /*
378 * Open the lower MAC and assign its initial bandwidth and
379 * MAC address. We do this here during VNIC creation and
380 * do not wait until the upper MAC client open so that we
381 * can validate the VNIC creation parameters (bandwidth,
382 * MAC address, etc) and reserve a factory MAC address if
383 * one was requested.
384 */
385 err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh);
386 if (err != 0)
387 goto bail;
388
389 /*
390 * VNIC(vlan) over VNICs(vlans) is not supported.
391 */
392 if (mac_is_vnic(vnic->vn_lower_mh)) {
393 err = EINVAL;
394 goto bail;
395 }
396
397 /* only ethernet support for now */
398 minfop = mac_info(vnic->vn_lower_mh);
399 if (minfop->mi_nativemedia != DL_ETHER) {
400 err = ENOTSUP;
401 goto bail;
402 }
403
404 (void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL,
405 NULL);
406 err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch,
407 vnic_name, MAC_OPEN_FLAGS_IS_VNIC);
408 if (err != 0)
409 goto bail;
410
411 if (mrp != NULL) {
412 if ((mrp->mrp_mask & MRP_RX_RINGS) != 0 ||
413 (mrp->mrp_mask & MRP_TX_RINGS) != 0) {
414 req_hwgrp_flag = B_TRUE;
415 }
416 err = mac_client_set_resources(vnic->vn_mch, mrp);
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 /* register with the MAC module */
463 if ((mac = mac_alloc(MAC_VERSION)) == NULL)
464 goto bail;
465
466 mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
467 mac->m_driver = vnic;
468 mac->m_dip = vnic_get_dip();
469 mac->m_instance = (uint_t)-1;
470 mac->m_src_addr = vnic->vn_addr;
471 mac->m_callbacks = &vnic_m_callbacks;
472
473 if (!is_anchor) {
474 /*
475 * If this is a VNIC based VLAN, then we check for the
476 * margin unless it has been created with the force
477 * flag. If we are configuring a VLAN over an etherstub,
478 * we don't check the margin even if force is not set.
479 */
480 if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) {
481 if (vid != VLAN_ID_NONE)
482 vnic->vn_force = B_TRUE;
483 /*
484 * As the current margin size of the underlying mac is
485 * used to determine the margin size of the VNIC
486 * itself, request the underlying mac not to change
487 * to a smaller margin size.
488 */
489 err = mac_margin_add(vnic->vn_lower_mh,
490 &vnic->vn_margin, B_TRUE);
491 ASSERT(err == 0);
492 } else {
493 vnic->vn_margin = VLAN_TAGSZ;
494 err = mac_margin_add(vnic->vn_lower_mh,
495 &vnic->vn_margin, B_FALSE);
496 if (err != 0) {
497 mac_free(mac);
498 if (diag != NULL)
499 *diag = VNIC_IOC_DIAG_MACMARGIN_INVALID;
500 goto bail;
501 }
502 }
503
504 mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu,
505 &mac->m_max_sdu);
506 } else {
507 vnic->vn_margin = VLAN_TAGSZ;
508 mac->m_min_sdu = ANCHOR_VNIC_MIN_MTU;
509 mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU;
510 }
511
512 mac->m_margin = vnic->vn_margin;
513
514 err = mac_register(mac, &vnic->vn_mh);
515 mac_free(mac);
516 if (err != 0) {
517 VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
518 vnic->vn_margin) == 0);
519 goto bail;
520 }
521
522 /* Set the VNIC's MAC in the client */
523 if (!is_anchor)
524 mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh, mrp);
525
526 err = dls_devnet_create(vnic->vn_mh, vnic->vn_id, crgetzoneid(credp));
527 if (err != 0) {
528 VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
529 vnic->vn_margin) == 0);
530 (void) mac_unregister(vnic->vn_mh);
531 goto bail;
532 }
533
534 /* add new VNIC to hash table */
535 err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id),
536 (mod_hash_val_t)vnic);
537 ASSERT(err == 0);
538 vnic_count++;
539
540 vnic->vn_enabled = B_TRUE;
541 rw_exit(&vnic_lock);
542
543 return (0);
544
545 bail:
546 rw_exit(&vnic_lock);
547 if (!is_anchor) {
548 if (vnic->vn_mnh != NULL)
549 (void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
550 if (vnic->vn_muh != NULL)
551 (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
552 if (vnic->vn_mch != NULL)
553 mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
554 if (vnic->vn_lower_mh != NULL)
555 mac_close(vnic->vn_lower_mh);
556 }
557
558 kmem_cache_free(vnic_cache, vnic);
559 return (err);
560 }
561
562 /*
563 * Modify the properties of an existing VNIC.
564 */
565 /* ARGSUSED */
566 int
567 vnic_dev_modify(datalink_id_t vnic_id, uint_t modify_mask,
568 vnic_mac_addr_type_t mac_addr_type, uint_t mac_len, uchar_t *mac_addr,
569 uint_t mac_slot, mac_resource_props_t *mrp)
570 {
571 vnic_t *vnic = NULL;
572
573 rw_enter(&vnic_lock, RW_WRITER);
574
575 if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
576 (mod_hash_val_t *)&vnic) != 0) {
577 rw_exit(&vnic_lock);
578 return (ENOENT);
579 }
580
581 rw_exit(&vnic_lock);
582
583 return (0);
584 }
585
586 /* ARGSUSED */
587 int
588 vnic_dev_delete(datalink_id_t vnic_id, uint32_t flags, cred_t *credp)
589 {
590 vnic_t *vnic = NULL;
591 mod_hash_val_t val;
592 datalink_id_t tmpid;
593 int rc;
594
595 rw_enter(&vnic_lock, RW_WRITER);
596
597 if (mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
598 (mod_hash_val_t *)&vnic) != 0) {
599 rw_exit(&vnic_lock);
600 return (ENOENT);
601 }
602
603 if ((rc = dls_devnet_destroy(vnic->vn_mh, &tmpid, B_TRUE)) != 0) {
604 rw_exit(&vnic_lock);
605 return (rc);
606 }
607
608 ASSERT(vnic_id == tmpid);
609
610 /*
611 * We cannot unregister the MAC yet. Unregistering would
612 * free up mac_impl_t which should not happen at this time.
613 * So disable mac_impl_t by calling mac_disable(). This will prevent
614 * any new claims on mac_impl_t.
615 */
616 if ((rc = mac_disable(vnic->vn_mh)) != 0) {
617 (void) dls_devnet_create(vnic->vn_mh, vnic_id,
618 crgetzoneid(credp));
619 rw_exit(&vnic_lock);
620 return (rc);
621 }
622
623 vnic->vn_enabled = B_FALSE;
624 (void) mod_hash_remove(vnic_hash, VNIC_HASH_KEY(vnic_id), &val);
625 ASSERT(vnic == (vnic_t *)val);
626 vnic_count--;
627 rw_exit(&vnic_lock);
628
629 /*
630 * XXX-nicolas shouldn't have a void cast here, if it's
631 * expected that the function will never fail, then we should
632 * have an ASSERT().
633 */
634 (void) mac_unregister(vnic->vn_mh);
635
636 if (vnic->vn_lower_mh != NULL) {
637 /*
638 * Check if MAC address for the vnic was obtained from the
639 * factory MAC addresses. If yes, release it.
640 */
641 if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY) {
642 (void) mac_addr_factory_release(vnic->vn_mch,
643 vnic->vn_slot_id);
644 }
645 (void) mac_margin_remove(vnic->vn_lower_mh, vnic->vn_margin);
646 (void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
647 (void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
648 mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
649 mac_close(vnic->vn_lower_mh);
650 }
651
652 kmem_cache_free(vnic_cache, vnic);
653 return (0);
654 }
655
656 /* ARGSUSED */
657 mblk_t *
658 vnic_m_tx(void *arg, mblk_t *mp_chain)
659 {
660 /*
661 * This function could be invoked for an anchor VNIC when sending
662 * broadcast and multicast packets, and unicast packets which did
663 * not match any local known destination.
664 */
665 freemsgchain(mp_chain);
666 return (NULL);
667 }
668
669 /*ARGSUSED*/
670 static void
671 vnic_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
672 {
673 miocnak(q, mp, 0, ENOTSUP);
674 }
675
676 /*
677 * This entry point cannot be passed-through, since it is invoked
678 * for the per-VNIC kstats which must be exported independently
679 * of the existence of VNIC MAC clients.
680 */
681 static int
682 vnic_m_stat(void *arg, uint_t stat, uint64_t *val)
683 {
684 vnic_t *vnic = arg;
685 int rval = 0;
686
687 if (vnic->vn_lower_mh == NULL) {
688 /*
689 * It's an anchor VNIC, which does not have any
690 * statistics in itself.
691 */
692 return (ENOTSUP);
693 }
694
695 /*
696 * ENOTSUP must be reported for unsupported stats, the VNIC
697 * driver reports a subset of the stats that would
698 * be returned by a real piece of hardware.
699 */
700
701 switch (stat) {
702 case MAC_STAT_LINK_STATE:
703 case MAC_STAT_LINK_UP:
704 case MAC_STAT_PROMISC:
705 case MAC_STAT_IFSPEED:
706 case MAC_STAT_MULTIRCV:
707 case MAC_STAT_MULTIXMT:
708 case MAC_STAT_BRDCSTRCV:
709 case MAC_STAT_BRDCSTXMT:
710 case MAC_STAT_OPACKETS:
711 case MAC_STAT_OBYTES:
712 case MAC_STAT_IERRORS:
713 case MAC_STAT_OERRORS:
714 case MAC_STAT_RBYTES:
715 case MAC_STAT_IPACKETS:
716 *val = mac_client_stat_get(vnic->vn_mch, stat);
717 break;
718 default:
719 rval = ENOTSUP;
720 }
721
722 return (rval);
723 }
724
725 /*
726 * Invoked by the upper MAC to retrieve the lower MAC client handle
727 * corresponding to a VNIC. A pointer to this function is obtained
728 * by the upper MAC via capability query.
729 *
730 * XXX-nicolas Note: this currently causes all VNIC MAC clients to
731 * receive the same MAC client handle for the same VNIC. This is ok
732 * as long as we have only one VNIC MAC client which sends and
733 * receives data, but we don't currently enforce this at the MAC layer.
734 */
735 static void *
736 vnic_mac_client_handle(void *vnic_arg)
737 {
738 vnic_t *vnic = vnic_arg;
739
740 return (vnic->vn_mch);
741 }
742
743
744 /*
745 * Return information about the specified capability.
746 */
747 /* ARGSUSED */
748 static boolean_t
749 vnic_m_capab_get(void *arg, mac_capab_t cap, void *cap_data)
750 {
751 vnic_t *vnic = arg;
752
753 switch (cap) {
754 case MAC_CAPAB_HCKSUM: {
755 uint32_t *hcksum_txflags = cap_data;
756
757 *hcksum_txflags = vnic->vn_hcksum_txflags &
758 (HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM |
759 HCKSUM_INET_PARTIAL);
760 break;
761 }
762 case MAC_CAPAB_VNIC: {
763 mac_capab_vnic_t *vnic_capab = cap_data;
764
765 if (vnic->vn_lower_mh == NULL) {
766 /*
767 * It's an anchor VNIC, we don't have an underlying
768 * NIC and MAC client handle.
769 */
770 return (B_FALSE);
771 }
772
773 if (vnic_capab != NULL) {
774 vnic_capab->mcv_arg = vnic;
775 vnic_capab->mcv_mac_client_handle =
776 vnic_mac_client_handle;
777 }
778 break;
779 }
780 case MAC_CAPAB_ANCHOR_VNIC: {
781 /* since it's an anchor VNIC we don't have lower mac handle */
782 if (vnic->vn_lower_mh == NULL) {
783 ASSERT(vnic->vn_link_id == 0);
784 return (B_TRUE);
785 }
786 return (B_FALSE);
787 }
788 case MAC_CAPAB_NO_NATIVEVLAN:
789 return (B_FALSE);
790 case MAC_CAPAB_NO_ZCOPY:
791 return (B_TRUE);
792 case MAC_CAPAB_VRRP: {
793 mac_capab_vrrp_t *vrrp_capab = cap_data;
794
795 if (vnic->vn_vrid != 0) {
796 if (vrrp_capab != NULL)
797 vrrp_capab->mcv_af = vnic->vn_af;
798 return (B_TRUE);
799 }
800 return (B_FALSE);
801 }
802 default:
803 return (B_FALSE);
804 }
805 return (B_TRUE);
806 }
807
808 /* ARGSUSED */
809 static int
810 vnic_m_start(void *arg)
811 {
812 return (0);
813 }
814
815 /* ARGSUSED */
816 static void
817 vnic_m_stop(void *arg)
818 {
819 }
820
821 /* ARGSUSED */
822 static int
823 vnic_m_promisc(void *arg, boolean_t on)
824 {
825 return (0);
826 }
827
828 /* ARGSUSED */
829 static int
830 vnic_m_multicst(void *arg, boolean_t add, const uint8_t *addrp)
831 {
832 return (0);
833 }
834
835 static int
836 vnic_m_unicst(void *arg, const uint8_t *macaddr)
837 {
838 vnic_t *vnic = arg;
839
840 return (mac_vnic_unicast_set(vnic->vn_mch, macaddr));
841 }
842
843 /*
844 * Callback functions for set/get of properties
845 */
846 /*ARGSUSED*/
847 static int
848 vnic_m_setprop(void *m_driver, const char *pr_name, mac_prop_id_t pr_num,
849 uint_t pr_valsize, const void *pr_val)
850 {
851 int err = 0;
852 vnic_t *vn = m_driver;
853
854 switch (pr_num) {
855 case MAC_PROP_MTU: {
856 uint32_t mtu;
857
858 /* allow setting MTU only on an etherstub */
859 if (vn->vn_link_id != DATALINK_INVALID_LINKID) {
860 err = ENOTSUP;
861 break;
862 }
863
864 if (pr_valsize < sizeof (mtu)) {
865 err = EINVAL;
866 break;
867 }
868 bcopy(pr_val, &mtu, sizeof (mtu));
869 if (mtu < ANCHOR_VNIC_MIN_MTU || mtu > ANCHOR_VNIC_MAX_MTU) {
870 err = EINVAL;
871 break;
872 }
873 err = mac_maxsdu_update(vn->vn_mh, mtu);
874 break;
875 }
876 case MAC_PROP_VN_PROMISC_FILTERED: {
877 boolean_t filtered;
878
879 if (pr_valsize < sizeof (filtered)) {
880 err = EINVAL;
881 break;
882 }
883
884 bcopy(pr_val, &filtered, sizeof (filtered));
885 mac_set_promisc_filtered(vn->vn_mch, filtered);
886 }
887 default:
888 err = ENOTSUP;
889 break;
890 }
891 return (err);
892 }
893
894 static int
895 vnic_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
896 uint_t pr_valsize, void *pr_val)
897 {
898 vnic_t *vn = arg;
899 int ret = 0;
900 boolean_t out;
901
902 switch (pr_num) {
903 case MAC_PROP_VN_PROMISC_FILTERED:
904 out = mac_get_promisc_filtered(vn->vn_mch);
905 ASSERT(pr_valsize >= sizeof (boolean_t));
906 bcopy(&out, pr_val, sizeof (boolean_t));
907 break;
908 default:
909 ret = EINVAL;
910 break;
911 }
912
913 return (ret);
914 }
915
916 /* ARGSUSED */
917 static void vnic_m_propinfo(void *m_driver, const char *pr_name,
918 mac_prop_id_t pr_num, mac_prop_info_handle_t prh)
919 {
920 vnic_t *vn = m_driver;
921
922 /* MTU setting allowed only on an etherstub */
923 if (vn->vn_link_id != DATALINK_INVALID_LINKID)
924 return;
925
926 switch (pr_num) {
927 case MAC_PROP_MTU:
928 mac_prop_info_set_range_uint32(prh,
929 ANCHOR_VNIC_MIN_MTU, ANCHOR_VNIC_MAX_MTU);
930 break;
931 }
932 }
933
934
935 int
936 vnic_info(vnic_info_t *info, cred_t *credp)
937 {
938 vnic_t *vnic;
939 int err;
940
941 /* Make sure that the VNIC link is visible from the caller's zone. */
942 if (!dls_devnet_islinkvisible(info->vn_vnic_id, crgetzoneid(credp)))
943 return (ENOENT);
944
945 rw_enter(&vnic_lock, RW_WRITER);
946
947 err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(info->vn_vnic_id),
948 (mod_hash_val_t *)&vnic);
949 if (err != 0) {
950 rw_exit(&vnic_lock);
951 return (ENOENT);
952 }
953
954 info->vn_link_id = vnic->vn_link_id;
955 info->vn_mac_addr_type = vnic->vn_addr_type;
956 info->vn_mac_len = vnic->vn_addr_len;
957 bcopy(vnic->vn_addr, info->vn_mac_addr, MAXMACADDRLEN);
958 info->vn_mac_slot = vnic->vn_slot_id;
959 info->vn_mac_prefix_len = 0;
960 info->vn_vid = vnic->vn_vid;
961 info->vn_force = vnic->vn_force;
962 info->vn_vrid = vnic->vn_vrid;
963 info->vn_af = vnic->vn_af;
964
965 bzero(&info->vn_resource_props, sizeof (mac_resource_props_t));
966 if (vnic->vn_mch != NULL)
967 mac_resource_ctl_get(vnic->vn_mch, &info->vn_resource_props);
968
969 rw_exit(&vnic_lock);
970 return (0);
971 }
972
973 static void
974 vnic_notify_cb(void *arg, mac_notify_type_t type)
975 {
976 vnic_t *vnic = arg;
977
978 /*
979 * Do not deliver notifications if the vnic is not fully initialized
980 * or is in process of being torn down.
981 */
982 if (!vnic->vn_enabled)
983 return;
984
985 switch (type) {
986 case MAC_NOTE_UNICST:
987 /*
988 * Only the VLAN VNIC needs to be notified with primary MAC
989 * address change.
990 */
991 if (vnic->vn_addr_type != VNIC_MAC_ADDR_TYPE_PRIMARY)
992 return;
993
994 /* the unicast MAC address value */
995 mac_unicast_primary_get(vnic->vn_lower_mh, vnic->vn_addr);
996
997 /* notify its upper layer MAC about MAC address change */
998 mac_unicst_update(vnic->vn_mh, (const uint8_t *)vnic->vn_addr);
999 break;
1000
1001 case MAC_NOTE_LINK:
1002 mac_link_update(vnic->vn_mh,
1003 mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE));
1004 break;
1005
1006 default:
1007 break;
1008 }
1009 }