예제 #1
0
/*
 * DL_UNBIND_REQ
 */
static void
proto_unbind_req(dld_str_t *dsp, mblk_t *mp)
{
	queue_t		*q = dsp->ds_wq;
	t_uscalar_t	dl_err;
	mac_perim_handle_t	mph;

	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
		dl_err = DL_BADPRIM;
		goto failed;
	}

	if (dsp->ds_dlstate != DL_IDLE) {
		dl_err = DL_OUTSTATE;
		goto failed;
	}

	mutex_enter(&dsp->ds_lock);
	while (dsp->ds_datathr_cnt != 0)
		cv_wait(&dsp->ds_datathr_cv, &dsp->ds_lock);

	dsp->ds_dlstate = DL_UNBIND_PENDING;
	mutex_exit(&dsp->ds_lock);

	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
	/*
	 * Unbind the channel to stop packets being received.
	 */
	dls_unbind(dsp);

	/*
	 * Disable polling mode, if it is enabled.
	 */
	(void) dld_capab_poll_disable(dsp, NULL);

	/*
	 * Clear LSO flags.
	 */
	dsp->ds_lso = B_FALSE;
	dsp->ds_lso_max = 0;

	/*
	 * Clear the receive callback.
	 */
	dls_rx_set(dsp, NULL, NULL);
	dsp->ds_direct = B_FALSE;

	/*
	 * Set the mode back to the default (unitdata).
	 */
	dsp->ds_mode = DLD_UNITDATA;
	dsp->ds_dlstate = DL_UNBOUND;

	dls_active_clear(dsp, B_FALSE);
	mac_perim_exit(mph);
	dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ);
	return;
failed:
	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
}
예제 #2
0
/*
 * DL_ENABMULTI_REQ
 */
static void
proto_enabmulti_req(dld_str_t *dsp, mblk_t *mp)
{
	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)mp->b_rptr;
	int		err = 0;
	t_uscalar_t	dl_err;
	queue_t		*q = dsp->ds_wq;
	mac_perim_handle_t	mph;

	if (dsp->ds_dlstate == DL_UNATTACHED ||
	    DL_ACK_PENDING(dsp->ds_dlstate)) {
		dl_err = DL_OUTSTATE;
		goto failed;
	}

	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
		dl_err = DL_BADPRIM;
		goto failed;
	}

	mac_perim_enter_by_mh(dsp->ds_mh, &mph);

	if ((dsp->ds_dmap == NULL) && (err = dls_active_set(dsp)) != 0) {
		dl_err = DL_SYSERR;
		goto failed2;
	}

	err = dls_multicst_add(dsp, mp->b_rptr + dlp->dl_addr_offset);
	if (err != 0) {
		switch (err) {
		case EINVAL:
			dl_err = DL_BADADDR;
			err = 0;
			break;
		case ENOSPC:
			dl_err = DL_TOOMANY;
			err = 0;
			break;
		default:
			dl_err = DL_SYSERR;
			break;
		}
		if (dsp->ds_dmap == NULL)
			dls_active_clear(dsp, B_FALSE);
		goto failed2;
	}

	mac_perim_exit(mph);

	dlokack(q, mp, DL_ENABMULTI_REQ);
	return;

failed2:
	mac_perim_exit(mph);
failed:
	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
}
예제 #3
0
static int
mac_bpf_getzone(uintptr_t handle, zoneid_t *zip)
{
	mac_perim_handle_t mph;
	int error;

	mac_perim_enter_by_mh((mac_handle_t)handle, &mph);
	error = dls_link_getzid(mac_name((mac_handle_t)handle), zip);
	mac_perim_exit(mph);
	return (error);
}
예제 #4
0
/*
 * 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);
}
예제 #5
0
static int
dld_capab_perim(dld_str_t *dsp, void *data, uint_t flags)
{
	switch (flags) {
	case DLD_ENABLE:
		mac_perim_enter_by_mh(dsp->ds_mh, (mac_perim_handle_t *)data);
		return (0);

	case DLD_DISABLE:
		mac_perim_exit((mac_perim_handle_t)data);
		return (0);

	case DLD_QUERY:
		return (mac_perim_held(dsp->ds_mh));
	}
	return (0);
}
예제 #6
0
/* ARGSUSED */
static void vnic_m_propinfo(void *m_driver, const char *pr_name,
    mac_prop_id_t pr_num, mac_prop_info_handle_t prh)
{
	vnic_t		*vn = m_driver;

	switch (pr_num) {
	case MAC_PROP_MTU:
		if (vn->vn_link_id == DATALINK_INVALID_LINKID) {
			mac_prop_info_set_range_uint32(prh,
			    ANCHOR_VNIC_MIN_MTU, ANCHOR_VNIC_MAX_MTU);
		} else {
			uint32_t		max;
			mac_perim_handle_t	mph;
			mac_propval_range_t	range;

			/*
			 * The valid range for a VNIC's MTU is the minimum that
			 * the device supports and the current value of the
			 * device. A VNIC cannot increase the current MTU of the
			 * device. Therefore we need to get the range from the
			 * propinfo endpoint and current mtu from the
			 * traditional property endpoint.
			 */
			mac_perim_enter_by_mh(vn->vn_lower_mh, &mph);
			if (mac_get_prop(vn->vn_lower_mh, MAC_PROP_MTU, "mtu",
			    &max, sizeof (uint32_t)) != 0) {
				mac_perim_exit(mph);
				return;
			}

			range.mpr_count = 1;
			if (mac_prop_info(vn->vn_lower_mh, MAC_PROP_MTU, "mtu",
			    NULL, 0, &range, NULL) != 0) {
				mac_perim_exit(mph);
				return;
			}

			mac_prop_info_set_default_uint32(prh, max);
			mac_prop_info_set_range_uint32(prh,
			    range.mpr_range_uint32[0].mpur_min, max);
			mac_perim_exit(mph);
		}
		break;
	}
}
예제 #7
0
/*
 * DL_SET_PHYS_ADDR_REQ
 */
static void
proto_setphysaddr_req(dld_str_t *dsp, mblk_t *mp)
{
	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)mp->b_rptr;
	int		err = 0;
	t_uscalar_t	dl_err;
	queue_t		*q = dsp->ds_wq;
	mac_perim_handle_t	mph;

	if (dsp->ds_dlstate == DL_UNATTACHED ||
	    DL_ACK_PENDING(dsp->ds_dlstate)) {
		dl_err = DL_OUTSTATE;
		goto failed;
	}

	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
		dl_err = DL_BADPRIM;
		goto failed;
	}

	mac_perim_enter_by_mh(dsp->ds_mh, &mph);

	if ((err = dls_active_set(dsp)) != 0) {
		dl_err = DL_SYSERR;
		goto failed2;
	}

	/*
	 * If mac-nospoof is enabled and the link is owned by a
	 * non-global zone, changing the mac address is not allowed.
	 */
	if (dsp->ds_dlp->dl_zid != GLOBAL_ZONEID &&
	    mac_protect_enabled(dsp->ds_mch, MPT_MACNOSPOOF)) {
		dls_active_clear(dsp, B_FALSE);
		err = EACCES;
		goto failed2;
	}

	err = mac_unicast_primary_set(dsp->ds_mh,
	    mp->b_rptr + dlp->dl_addr_offset);
	if (err != 0) {
		switch (err) {
		case EINVAL:
			dl_err = DL_BADADDR;
			err = 0;
			break;

		default:
			dl_err = DL_SYSERR;
			break;
		}
		dls_active_clear(dsp, B_FALSE);
		goto failed2;

	}

	mac_perim_exit(mph);

	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
	return;

failed2:
	mac_perim_exit(mph);
failed:
	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
}
예제 #8
0
/*
 * DL_PROMISCOFF_REQ
 */
static void
proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp)
{
	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)mp->b_rptr;
	int		err = 0;
	t_uscalar_t	dl_err;
	uint32_t	promisc_saved;
	queue_t		*q = dsp->ds_wq;
	mac_perim_handle_t	mph;

	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
		dl_err = DL_BADPRIM;
		goto failed;
	}

	if (dsp->ds_dlstate == DL_UNATTACHED ||
	    DL_ACK_PENDING(dsp->ds_dlstate)) {
		dl_err = DL_OUTSTATE;
		goto failed;
	}

	promisc_saved = dsp->ds_promisc;
	switch (dlp->dl_level) {
	case DL_PROMISC_SAP:
		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
			dl_err = DL_NOTENAB;
			goto failed;
		}
		dsp->ds_promisc &= ~DLS_PROMISC_SAP;
		break;

	case DL_PROMISC_MULTI:
		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
			dl_err = DL_NOTENAB;
			goto failed;
		}
		dsp->ds_promisc &= ~DLS_PROMISC_MULTI;
		break;

	case DL_PROMISC_PHYS:
		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
			dl_err = DL_NOTENAB;
			goto failed;
		}
		dsp->ds_promisc &= ~DLS_PROMISC_PHYS;
		break;

	default:
		dl_err = DL_NOTSUPPORTED;
		goto failed;
	}

	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
	/*
	 * Adjust channel promiscuity.
	 */
	err = dls_promisc(dsp, promisc_saved);

	if (err != 0) {
		mac_perim_exit(mph);
		dl_err = DL_SYSERR;
		goto failed;
	}

	if (dsp->ds_promisc == 0)
		dls_active_clear(dsp, B_FALSE);

	mac_perim_exit(mph);

	dlokack(q, mp, DL_PROMISCOFF_REQ);
	return;
failed:
	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
}
예제 #9
0
/*
 * DL_BIND_REQ
 */
static void
proto_bind_req(dld_str_t *dsp, mblk_t *mp)
{
	dl_bind_req_t	*dlp = (dl_bind_req_t *)mp->b_rptr;
	int		err = 0;
	uint8_t		dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)];
	uint_t		dlsap_addr_length;
	t_uscalar_t	dl_err;
	t_scalar_t	sap;
	queue_t		*q = dsp->ds_wq;
	mac_perim_handle_t	mph;
	void		*mdip;
	int32_t		intr_cpu;

	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
		dl_err = DL_BADPRIM;
		goto failed;
	}

	if (dlp->dl_xidtest_flg != 0) {
		dl_err = DL_NOAUTO;
		goto failed;
	}

	if (dlp->dl_service_mode != DL_CLDLS) {
		dl_err = DL_UNSUPPORTED;
		goto failed;
	}

	if (dsp->ds_dlstate != DL_UNBOUND) {
		dl_err = DL_OUTSTATE;
		goto failed;
	}

	mac_perim_enter_by_mh(dsp->ds_mh, &mph);

	if ((err = dls_active_set(dsp)) != 0) {
		dl_err = DL_SYSERR;
		goto failed2;
	}

	dsp->ds_dlstate = DL_BIND_PENDING;
	/*
	 * Set the receive callback.
	 */
	dls_rx_set(dsp, (dsp->ds_mode == DLD_RAW) ?
	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);

	/*
	 * Bind the channel such that it can receive packets.
	 */
	sap = dlp->dl_sap;
	dsp->ds_nonip = !check_mod_above(dsp->ds_rq, "ip") &&
	    !check_mod_above(dsp->ds_rq, "arp");

	err = dls_bind(dsp, sap);
	if (err != 0) {
		switch (err) {
		case EINVAL:
			dl_err = DL_BADADDR;
			err = 0;
			break;
		default:
			dl_err = DL_SYSERR;
			break;
		}

		dsp->ds_dlstate = DL_UNBOUND;
		dls_active_clear(dsp, B_FALSE);
		goto failed2;
	}

	intr_cpu = mac_client_intr_cpu(dsp->ds_mch);
	mdip = mac_get_devinfo(dsp->ds_mh);
	mac_perim_exit(mph);

	/*
	 * We do this after we get out of the perim to avoid deadlocks
	 * etc. since part of mac_client_retarget_intr is to walk the
	 * device tree in order to find and retarget the interrupts.
	 */
	if (intr_cpu != -1)
		mac_client_set_intr_cpu(mdip, dsp->ds_mch, intr_cpu);

	/*
	 * Copy in MAC address.
	 */
	dlsap_addr_length = dsp->ds_mip->mi_addr_length;
	mac_unicast_primary_get(dsp->ds_mh, dlsap_addr);

	/*
	 * Copy in the SAP.
	 */
	*(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap;
	dlsap_addr_length += sizeof (uint16_t);

	dsp->ds_dlstate = DL_IDLE;
	dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0);
	return;

failed2:
	mac_perim_exit(mph);
failed:
	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
}