Example #1
0
static int
mac_bpf_client_open(uintptr_t mhandle, uintptr_t *chandlep)
{
	return (mac_client_open((mac_handle_t)mhandle,
	    (mac_client_handle_t *)chandlep,  NULL,
	    MAC_OPEN_FLAGS_USE_DATALINK_NAME));
}
Example #2
0
/*
 * Open a MAC client for a port or an interface.
 * The flags and their purpose as below:
 *
 *	MAC_OPEN_FLAGS_SHARES_DESIRED -- This flag is used to indicate
 *	that a port desires a Share. This will be the case with the
 *	the ports that have hybrid mode enabled. This will only cause
 *	MAC layer to allocate a share and corresponding resources
 *	ahead of time. Ports that are not HybridIO enabled are
 *	associated with default group & resources.
 *
 *	MAC_UNICAST_TAG_DISABLE -- This flag is used for VLAN
 *	support. It will cause MAC to not add any tags, but expect
 *	vsw to tag the packets.
 *
 *	MAC_UNICAST_STRIP_DISABLE -- This flag is used for VLAN
 *	support. It will case the MAC layer to not strip the tags.
 *	Vsw may have to strip the tag for pvid case.
 */
static int
vsw_maccl_open(vsw_t *vswp, vsw_port_t *port, int type)
{
	int		rv = 0;
	int		instance;
	char		mac_cl_name[MAXNAMELEN];
	const char	*dev_name;
	mac_client_handle_t *mchp;
	uint64_t flags = 0;

	ASSERT(MUTEX_HELD(&vswp->mac_lock));
	if (vswp->mh == NULL) {
		/*
		 * In case net-dev is changed (either set to nothing or
		 * using aggregation device), return success here as the
		 * timeout mechanism will handle it.
		 */
		return (0);
	}

	mchp = (type == VSW_LOCALDEV) ? &vswp->mch : &port->p_mch;
	if (*mchp != NULL) {
		/* already open */
		return (0);
	}
	dev_name = ddi_driver_name(vswp->dip);
	instance = ddi_get_instance(vswp->dip);
	if (type == VSW_VNETPORT) {
		if (port->p_hio_enabled)
			flags |= MAC_OPEN_FLAGS_SHARES_DESIRED;
		(void) snprintf(mac_cl_name, MAXNAMELEN, "%s%d%s%d", dev_name,
		    instance, "_port", port->p_instance);
	} else {
		(void) snprintf(mac_cl_name, MAXNAMELEN, "%s%s%d",
		    dev_name, "_if", instance);
	}

	rv = mac_client_open(vswp->mh, mchp, mac_cl_name, flags);
	if (rv != 0) {
		cmn_err(CE_NOTE, "!vsw%d:%s mac_client_open() failed\n",
		    vswp->instance, mac_cl_name);
	}

	if (type != VSW_VNETPORT || !port->p_hio_enabled)
		mac_client_set_rings(*mchp, MAC_RXRINGS_NONE, MAC_TXRINGS_NONE);

	return (rv);
}
/*
 * pfp_open_index is an internal function used to open a MAC device by
 * its index. Both a mac_handle_t and mac_client_handle_t are acquired
 * because some of the interfaces provided by the mac layer require either
 * only the mac_handle_t or both it and mac_handle_t.
 *
 * Whilst inside the kernel we can access data structures supporting any
 * zone, access to interfaces from non-global zones is restricted to those
 * interfaces (if any) that are exclusively assigned to a zone.
 */
static int
pfp_open_index(int index, mac_handle_t *mhp, mac_client_handle_t *mcip,
    cred_t *cred)
{
	mac_client_handle_t mch;
	zoneid_t ifzoneid;
	mac_handle_t mh;
	zoneid_t zoneid;
	int error;

	mh = 0;
	mch = 0;
	error = mac_open_by_linkid(index, &mh);
	if (error != 0)
		goto bad_open;

	error = mac_client_open(mh, &mch, NULL,
	    MAC_OPEN_FLAGS_USE_DATALINK_NAME);
	if (error != 0)
		goto bad_open;

	zoneid = crgetzoneid(cred);
	if (zoneid != GLOBAL_ZONEID) {
		mac_perim_handle_t perim;

		mac_perim_enter_by_mh(mh, &perim);
		error = dls_link_getzid(mac_client_name(mch), &ifzoneid);
		mac_perim_exit(perim);
		if (error != 0)
			goto bad_open;
		if (ifzoneid != zoneid) {
			error = EACCES;
			goto bad_open;
		}
	}

	*mcip = mch;
	*mhp = mh;

	return (0);
bad_open:
	if (mch != 0)
		mac_client_close(mch, 0);
	if (mh != 0)
		mac_close(mh);
	return (error);
}
Example #4
0
/*
 * Setup secondary MAC addresses on the vnic. Due to limitations in the mac
 * code, each mac address must be associated with a mac_client (and the
 * flow that goes along with the client) so we need to create those clients
 * here.
 */
static int
vnic_set_secondary_macs(vnic_t *vn, mac_secondary_addr_t *msa)
{
	int i, err;
	char primary_name[MAXNAMELEN];

	/* First, remove pre-existing secondaries */
	ASSERT(vn->vn_nhandles < MPT_MAXMACADDR);
	vnic_cleanup_secondary_macs(vn, vn->vn_nhandles);

	if (msa->ms_addrcnt == (uint32_t)-1)
		msa->ms_addrcnt = 0;

	vn->vn_nhandles = msa->ms_addrcnt;

	(void) dls_mgmt_get_linkinfo(vn->vn_id, primary_name, NULL, NULL, NULL);

	/*
	 * Now add the new secondary MACs
	 * Recall that the primary MAC address is the first element.
	 * The secondary clients are named after the primary with their
	 * index to distinguish them.
	 */
	for (i = 1; i <= vn->vn_nhandles; i++) {
		uint8_t *addr;
		mac_diag_t mac_diag;
		char secondary_name[MAXNAMELEN];

		(void) snprintf(secondary_name, sizeof (secondary_name),
		    "%s%02d", primary_name, i);

		err = mac_client_open(vn->vn_lower_mh, &vn->vn_mc_handles[i],
		    secondary_name, MAC_OPEN_FLAGS_IS_VNIC);
		if (err != 0) {
			/* Remove any that we successfully added */
			vnic_cleanup_secondary_macs(vn, --i);
			return (err);
		}

		/*
		 * Assign a MAC address to the VNIC
		 *
		 * Normally this would be done with vnic_unicast_add but since
		 * we know these are fixed adddresses, and since we need to
		 * save this in the proper array slot, we bypass that function
		 * and go direct.
		 */
		addr = msa->ms_addrs[i - 1];
		err = mac_unicast_add(vn->vn_mc_handles[i], addr, 0,
		    &vn->vn_mu_handles[i], vn->vn_vid, &mac_diag);
		if (err != 0) {
			/* Remove any that we successfully added */
			vnic_cleanup_secondary_macs(vn, i);
			return (err);
		}

		/*
		 * Setup the secondary the same way as the primary (i.e.
		 * receiver function/argument (e.g. i_dls_link_rx, mac_pkt_drop,
		 * etc.), the promisc list, and the resource controls).
		 */
		mac_secondary_dup(vn->vn_mc_handles[0], vn->vn_mc_handles[i]);
	}

	return (0);
}
Example #5
0
/* ARGSUSED */
int
vnic_dev_create(datalink_id_t vnic_id, datalink_id_t linkid,
    vnic_mac_addr_type_t *vnic_addr_type, int *mac_len, uchar_t *mac_addr,
    int *mac_slot, uint_t mac_prefix_len, uint16_t vid, vrid_t vrid,
    int af, mac_resource_props_t *mrp, uint32_t flags, vnic_ioc_diag_t *diag,
    cred_t *credp)
{
	vnic_t *vnic;
	mac_register_t *mac;
	int err;
	boolean_t is_anchor = ((flags & VNIC_IOC_CREATE_ANCHOR) != 0);
	char vnic_name[MAXNAMELEN];
	const mac_info_t *minfop;
	uint32_t req_hwgrp_flag = B_FALSE;

	*diag = VNIC_IOC_DIAG_NONE;

	rw_enter(&vnic_lock, RW_WRITER);

	/* does a VNIC with the same id already exist? */
	err = mod_hash_find(vnic_hash, VNIC_HASH_KEY(vnic_id),
	    (mod_hash_val_t *)&vnic);
	if (err == 0) {
		rw_exit(&vnic_lock);
		return (EEXIST);
	}

	vnic = kmem_cache_alloc(vnic_cache, KM_NOSLEEP);
	if (vnic == NULL) {
		rw_exit(&vnic_lock);
		return (ENOMEM);
	}

	bzero(vnic, sizeof (*vnic));

	vnic->vn_id = vnic_id;
	vnic->vn_link_id = linkid;
	vnic->vn_vrid = vrid;
	vnic->vn_af = af;

	if (!is_anchor) {
		if (linkid == DATALINK_INVALID_LINKID) {
			err = EINVAL;
			goto bail;
		}

		/*
		 * Open the lower MAC and assign its initial bandwidth and
		 * MAC address. We do this here during VNIC creation and
		 * do not wait until the upper MAC client open so that we
		 * can validate the VNIC creation parameters (bandwidth,
		 * MAC address, etc) and reserve a factory MAC address if
		 * one was requested.
		 */
		err = mac_open_by_linkid(linkid, &vnic->vn_lower_mh);
		if (err != 0)
			goto bail;

		/*
		 * VNIC(vlan) over VNICs(vlans) is not supported.
		 */
		if (mac_is_vnic(vnic->vn_lower_mh)) {
			err = EINVAL;
			goto bail;
		}

		/* only ethernet support for now */
		minfop = mac_info(vnic->vn_lower_mh);
		if (minfop->mi_nativemedia != DL_ETHER) {
			err = ENOTSUP;
			goto bail;
		}

		(void) dls_mgmt_get_linkinfo(vnic_id, vnic_name, NULL, NULL,
		    NULL);
		err = mac_client_open(vnic->vn_lower_mh, &vnic->vn_mch,
		    vnic_name, MAC_OPEN_FLAGS_IS_VNIC);
		if (err != 0)
			goto bail;

		/* assign a MAC address to the VNIC */

		err = vnic_unicast_add(vnic, *vnic_addr_type, mac_slot,
		    mac_prefix_len, mac_len, mac_addr, flags, diag, vid,
		    req_hwgrp_flag);
		if (err != 0) {
			vnic->vn_muh = NULL;
			if (diag != NULL && req_hwgrp_flag)
				*diag = VNIC_IOC_DIAG_NO_HWRINGS;
			goto bail;
		}

		/* register to receive notification from underlying MAC */
		vnic->vn_mnh = mac_notify_add(vnic->vn_lower_mh, vnic_notify_cb,
		    vnic);

		*vnic_addr_type = vnic->vn_addr_type;
		vnic->vn_addr_len = *mac_len;
		vnic->vn_vid = vid;

		bcopy(mac_addr, vnic->vn_addr, vnic->vn_addr_len);

		if (vnic->vn_addr_type == VNIC_MAC_ADDR_TYPE_FACTORY)
			vnic->vn_slot_id = *mac_slot;

		/*
		 * Set the initial VNIC capabilities. If the VNIC is created
		 * over MACs which does not support nactive vlan, disable
		 * VNIC's hardware checksum capability if its VID is not 0,
		 * since the underlying MAC would get the hardware checksum
		 * offset wrong in case of VLAN packets.
		 */
		if (vid == 0 || !mac_capab_get(vnic->vn_lower_mh,
		    MAC_CAPAB_NO_NATIVEVLAN, NULL)) {
			if (!mac_capab_get(vnic->vn_lower_mh, MAC_CAPAB_HCKSUM,
			    &vnic->vn_hcksum_txflags))
				vnic->vn_hcksum_txflags = 0;
		} else {
			vnic->vn_hcksum_txflags = 0;
		}
	}

	/* register with the MAC module */
	if ((mac = mac_alloc(MAC_VERSION)) == NULL)
		goto bail;

	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
	mac->m_driver = vnic;
	mac->m_dip = vnic_get_dip();
	mac->m_instance = (uint_t)-1;
	mac->m_src_addr = vnic->vn_addr;
	mac->m_callbacks = &vnic_m_callbacks;

	if (!is_anchor) {
		/*
		 * If this is a VNIC based VLAN, then we check for the
		 * margin unless it has been created with the force
		 * flag. If we are configuring a VLAN over an etherstub,
		 * we don't check the margin even if force is not set.
		 */
		if (vid == 0 || (flags & VNIC_IOC_CREATE_FORCE) != 0) {
			if (vid != VLAN_ID_NONE)
				vnic->vn_force = B_TRUE;
			/*
			 * As the current margin size of the underlying mac is
			 * used to determine the margin size of the VNIC
			 * itself, request the underlying mac not to change
			 * to a smaller margin size.
			 */
			err = mac_margin_add(vnic->vn_lower_mh,
			    &vnic->vn_margin, B_TRUE);
			ASSERT(err == 0);
		} else {
			vnic->vn_margin = VLAN_TAGSZ;
			err = mac_margin_add(vnic->vn_lower_mh,
			    &vnic->vn_margin, B_FALSE);
			if (err != 0) {
				mac_free(mac);
				if (diag != NULL)
					*diag = VNIC_IOC_DIAG_MACMARGIN_INVALID;
				goto bail;
			}
		}

		mac_sdu_get(vnic->vn_lower_mh, &mac->m_min_sdu,
		    &mac->m_max_sdu);
		err = mac_mtu_add(vnic->vn_lower_mh, &mac->m_max_sdu, B_FALSE);
		if (err != 0) {
			VERIFY(mac_margin_remove(vnic->vn_lower_mh,
			    vnic->vn_margin) == 0);
			mac_free(mac);
			if (diag != NULL)
				*diag = VNIC_IOC_DIAG_MACMTU_INVALID;
			goto bail;
		}
		vnic->vn_mtu = mac->m_max_sdu;
	} else {
		vnic->vn_margin = VLAN_TAGSZ;
		mac->m_min_sdu = 1;
		mac->m_max_sdu = ANCHOR_VNIC_MAX_MTU;
		vnic->vn_mtu = ANCHOR_VNIC_MAX_MTU;
	}

	mac->m_margin = vnic->vn_margin;

	err = mac_register(mac, &vnic->vn_mh);
	mac_free(mac);
	if (err != 0) {
		if (!is_anchor) {
			VERIFY(mac_mtu_remove(vnic->vn_lower_mh,
			    vnic->vn_mtu) == 0);
			VERIFY(mac_margin_remove(vnic->vn_lower_mh,
			    vnic->vn_margin) == 0);
		}
		goto bail;
	}

	/* Set the VNIC's MAC in the client */
	if (!is_anchor) {
		mac_set_upper_mac(vnic->vn_mch, vnic->vn_mh, mrp);

		if (mrp != NULL) {
			if ((mrp->mrp_mask & MRP_RX_RINGS) != 0 ||
			    (mrp->mrp_mask & MRP_TX_RINGS) != 0) {
				req_hwgrp_flag = B_TRUE;
			}
			err = mac_client_set_resources(vnic->vn_mch, mrp);
			if (err != 0) {
				VERIFY(mac_mtu_remove(vnic->vn_lower_mh,
				    vnic->vn_mtu) == 0);
				VERIFY(mac_margin_remove(vnic->vn_lower_mh,
				    vnic->vn_margin) == 0);
				(void) mac_unregister(vnic->vn_mh);
				goto bail;
			}
		}
	}

	err = dls_devnet_create(vnic->vn_mh, vnic->vn_id, crgetzoneid(credp));
	if (err != 0) {
		VERIFY(is_anchor || mac_margin_remove(vnic->vn_lower_mh,
		    vnic->vn_margin) == 0);
		if (!is_anchor) {
			VERIFY(mac_mtu_remove(vnic->vn_lower_mh,
			    vnic->vn_mtu) == 0);
			VERIFY(mac_margin_remove(vnic->vn_lower_mh,
			    vnic->vn_margin) == 0);
		}
		(void) mac_unregister(vnic->vn_mh);
		goto bail;
	}

	/* add new VNIC to hash table */
	err = mod_hash_insert(vnic_hash, VNIC_HASH_KEY(vnic_id),
	    (mod_hash_val_t)vnic);
	ASSERT(err == 0);
	vnic_count++;

	/*
	 * Now that we've enabled this VNIC, we should go through and update the
	 * link state by setting it to our parents.
	 */
	vnic->vn_enabled = B_TRUE;

	if (is_anchor) {
		mac_link_update(vnic->vn_mh, LINK_STATE_UP);
	} else {
		mac_link_update(vnic->vn_mh,
		    mac_client_stat_get(vnic->vn_mch, MAC_STAT_LINK_STATE));
	}

	rw_exit(&vnic_lock);

	return (0);

bail:
	rw_exit(&vnic_lock);
	if (!is_anchor) {
		if (vnic->vn_mnh != NULL)
			(void) mac_notify_remove(vnic->vn_mnh, B_TRUE);
		if (vnic->vn_muh != NULL)
			(void) mac_unicast_remove(vnic->vn_mch, vnic->vn_muh);
		if (vnic->vn_mch != NULL)
			mac_client_close(vnic->vn_mch, MAC_CLOSE_FLAGS_IS_VNIC);
		if (vnic->vn_lower_mh != NULL)
			mac_close(vnic->vn_lower_mh);
	}

	kmem_cache_free(vnic_cache, vnic);
	return (err);
}
Example #6
0
static boolean_t
xnbo_open_mac(xnb_t *xnbp, char *mac)
{
	xnbo_t *xnbop = xnbp->xnb_flavour_data;
	int err;
	const mac_info_t *mi;
	void (*rx_fn)(void *, mac_resource_handle_t, mblk_t *, boolean_t);
	struct ether_addr ea;
	uint_t max_sdu;
	mac_diag_t diag;

	if ((err = mac_open_by_linkname(mac, &xnbop->o_mh)) != 0) {
		cmn_err(CE_WARN, "xnbo_open_mac: "
		    "cannot open mac for link %s (%d)", mac, err);
		return (B_FALSE);
	}
	ASSERT(xnbop->o_mh != NULL);

	mi = mac_info(xnbop->o_mh);
	ASSERT(mi != NULL);

	if (mi->mi_media != DL_ETHER) {
		cmn_err(CE_WARN, "xnbo_open_mac: "
		    "device is not DL_ETHER (%d)", mi->mi_media);
		i_xnbo_close_mac(xnbp, B_TRUE);
		return (B_FALSE);
	}
	if (mi->mi_media != mi->mi_nativemedia) {
		cmn_err(CE_WARN, "xnbo_open_mac: "
		    "device media and native media mismatch (%d != %d)",
		    mi->mi_media, mi->mi_nativemedia);
		i_xnbo_close_mac(xnbp, B_TRUE);
		return (B_FALSE);
	}

	mac_sdu_get(xnbop->o_mh, NULL, &max_sdu);
	if (max_sdu > XNBMAXPKT) {
		cmn_err(CE_WARN, "xnbo_open_mac: mac device SDU too big (%d)",
		    max_sdu);
		i_xnbo_close_mac(xnbp, B_TRUE);
		return (B_FALSE);
	}

	/*
	 * MAC_OPEN_FLAGS_MULTI_PRIMARY is relevant when we are migrating a
	 * guest on the localhost itself. In this case we would have the MAC
	 * client open for the guest being migrated *and* also for the
	 * migrated guest (i.e. the former will be active till the migration
	 * is complete when the latter will be activated). This flag states
	 * that it is OK for mac_unicast_add to add the primary MAC unicast
	 * address multiple times.
	 */
	if (mac_client_open(xnbop->o_mh, &xnbop->o_mch, NULL,
	    MAC_OPEN_FLAGS_USE_DATALINK_NAME |
	    MAC_OPEN_FLAGS_MULTI_PRIMARY) != 0) {
		cmn_err(CE_WARN, "xnbo_open_mac: "
		    "error (%d) opening mac client", err);
		i_xnbo_close_mac(xnbp, B_TRUE);
		return (B_FALSE);
	}

	if (xnbop->o_need_rx_filter)
		rx_fn = xnbo_from_mac_filter;
	else
		rx_fn = xnbo_from_mac;

	err = mac_unicast_add_set_rx(xnbop->o_mch, NULL, MAC_UNICAST_PRIMARY,
	    &xnbop->o_mah, 0, &diag, xnbop->o_multicast_control ? rx_fn : NULL,
	    xnbp);
	if (err != 0) {
		cmn_err(CE_WARN, "xnbo_open_mac: failed to get the primary "
		    "MAC address of %s: %d", mac, err);
		i_xnbo_close_mac(xnbp, B_TRUE);
		return (B_FALSE);
	}
	if (!xnbop->o_multicast_control) {
		err = mac_promisc_add(xnbop->o_mch, MAC_CLIENT_PROMISC_ALL,
		    rx_fn, xnbp, &xnbop->o_mphp, MAC_PROMISC_FLAGS_NO_TX_LOOP |
		    MAC_PROMISC_FLAGS_VLAN_TAG_STRIP);
		if (err != 0) {
			cmn_err(CE_WARN, "xnbo_open_mac: "
			    "cannot enable promiscuous mode of %s: %d",
			    mac, err);
			i_xnbo_close_mac(xnbp, B_TRUE);
			return (B_FALSE);
		}
		xnbop->o_promiscuous = B_TRUE;
	}

	if (xnbop->o_need_setphysaddr) {
		err = mac_unicast_primary_set(xnbop->o_mh, xnbp->xnb_mac_addr);
		/* Warn, but continue on. */
		if (err != 0) {
			bcopy(xnbp->xnb_mac_addr, ea.ether_addr_octet,
			    ETHERADDRL);
			cmn_err(CE_WARN, "xnbo_open_mac: "
			    "cannot set MAC address of %s to "
			    "%s: %d", mac, ether_sprintf(&ea), err);
		}
	}

	if (!mac_capab_get(xnbop->o_mh, MAC_CAPAB_HCKSUM,
	    &xnbop->o_hcksum_capab))
		xnbop->o_hcksum_capab = 0;

	xnbop->o_running = B_TRUE;

	return (B_TRUE);
}