Home | History | Annotate | Download | only in softmac
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <sys/stropts.h>
     27 #include <sys/strsubr.h>
     28 #include <sys/callb.h>
     29 #include <sys/softmac_impl.h>
     30 
     31 int
     32 softmac_send_notify_req(softmac_lower_t *slp, uint32_t notifications)
     33 {
     34 	mblk_t		*reqmp;
     35 
     36 	/*
     37 	 * create notify req message and send it down
     38 	 */
     39 	reqmp = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO,
     40 	    DL_NOTIFY_REQ);
     41 	if (reqmp == NULL)
     42 		return (ENOMEM);
     43 
     44 	((dl_notify_req_t *)reqmp->b_rptr)->dl_notifications = notifications;
     45 
     46 	return (softmac_proto_tx(slp, reqmp, NULL));
     47 }
     48 
     49 int
     50 softmac_send_bind_req(softmac_lower_t *slp, uint_t sap)
     51 {
     52 	dl_bind_req_t	*bind;
     53 	mblk_t		*reqmp;
     54 
     55 	/*
     56 	 * create bind req message and send it down
     57 	 */
     58 	reqmp = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ);
     59 	if (reqmp == NULL)
     60 		return (ENOMEM);
     61 
     62 	bind = (dl_bind_req_t *)reqmp->b_rptr;
     63 	bind->dl_sap = sap;
     64 	bind->dl_conn_mgmt = 0;
     65 	bind->dl_max_conind = 0;
     66 	bind->dl_xidtest_flg = 0;
     67 	bind->dl_service_mode = DL_CLDLS;
     68 
     69 	return (softmac_proto_tx(slp, reqmp, NULL));
     70 }
     71 
     72 int
     73 softmac_send_unbind_req(softmac_lower_t *slp)
     74 {
     75 	mblk_t			*reqmp;
     76 
     77 	/*
     78 	 * create unbind req message and send it down
     79 	 */
     80 	reqmp = mexchange(NULL, NULL, DL_UNBIND_REQ_SIZE, M_PROTO,
     81 	    DL_UNBIND_REQ);
     82 	if (reqmp == NULL)
     83 		return (ENOMEM);
     84 
     85 	return (softmac_proto_tx(slp, reqmp, NULL));
     86 }
     87 
     88 int
     89 softmac_send_promisc_req(softmac_lower_t *slp, t_uscalar_t level, boolean_t on)
     90 {
     91 	mblk_t		*reqmp;
     92 	size_t		size;
     93 	t_uscalar_t	dl_prim;
     94 
     95 	/*
     96 	 * create promisc message and send it down
     97 	 */
     98 	if (on) {
     99 		dl_prim = DL_PROMISCON_REQ;
    100 		size = DL_PROMISCON_REQ_SIZE;
    101 	} else {
    102 		dl_prim = DL_PROMISCOFF_REQ;
    103 		size = DL_PROMISCOFF_REQ_SIZE;
    104 	}
    105 
    106 	reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim);
    107 	if (reqmp == NULL)
    108 		return (ENOMEM);
    109 
    110 	if (on)
    111 		((dl_promiscon_req_t *)reqmp->b_rptr)->dl_level = level;
    112 	else
    113 		((dl_promiscoff_req_t *)reqmp->b_rptr)->dl_level = level;
    114 
    115 	return (softmac_proto_tx(slp, reqmp, NULL));
    116 }
    117 
    118 int
    119 softmac_m_promisc(void *arg, boolean_t on)
    120 {
    121 	softmac_t		*softmac = arg;
    122 	softmac_lower_t		*slp = softmac->smac_lower;
    123 
    124 	ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
    125 	ASSERT(slp != NULL);
    126 	return (softmac_send_promisc_req(slp, DL_PROMISC_PHYS, on));
    127 }
    128 
    129 int
    130 softmac_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
    131 {
    132 	softmac_t		*softmac = arg;
    133 	softmac_lower_t		*slp;
    134 	dl_enabmulti_req_t	*enabmulti;
    135 	dl_disabmulti_req_t	*disabmulti;
    136 	mblk_t			*reqmp;
    137 	t_uscalar_t		dl_prim;
    138 	uint32_t		size, addr_length;
    139 
    140 	ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
    141 	/*
    142 	 * create multicst message and send it down
    143 	 */
    144 	addr_length = softmac->smac_addrlen;
    145 	if (add) {
    146 		size = sizeof (dl_enabmulti_req_t) + addr_length;
    147 		dl_prim = DL_ENABMULTI_REQ;
    148 	} else {
    149 		size = sizeof (dl_disabmulti_req_t) + addr_length;
    150 		dl_prim = DL_DISABMULTI_REQ;
    151 	}
    152 
    153 	reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim);
    154 	if (reqmp == NULL)
    155 		return (ENOMEM);
    156 
    157 	if (add) {
    158 		enabmulti = (dl_enabmulti_req_t *)reqmp->b_rptr;
    159 		enabmulti->dl_addr_offset = sizeof (dl_enabmulti_req_t);
    160 		enabmulti->dl_addr_length = addr_length;
    161 		(void) memcpy(&enabmulti[1], mca, addr_length);
    162 	} else {
    163 		disabmulti = (dl_disabmulti_req_t *)reqmp->b_rptr;
    164 		disabmulti->dl_addr_offset = sizeof (dl_disabmulti_req_t);
    165 		disabmulti->dl_addr_length = addr_length;
    166 		(void) memcpy(&disabmulti[1], mca, addr_length);
    167 	}
    168 
    169 	slp = softmac->smac_lower;
    170 	ASSERT(slp != NULL);
    171 	return (softmac_proto_tx(slp, reqmp, NULL));
    172 }
    173 
    174 int
    175 softmac_m_unicst(void *arg, const uint8_t *macaddr)
    176 {
    177 	softmac_t		*softmac = arg;
    178 	softmac_lower_t		*slp;
    179 	dl_set_phys_addr_req_t	*phyaddr;
    180 	mblk_t			*reqmp;
    181 	size_t			size;
    182 
    183 	ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
    184 	/*
    185 	 * create set_phys_addr message and send it down
    186 	 */
    187 	size = DL_SET_PHYS_ADDR_REQ_SIZE + softmac->smac_addrlen;
    188 	reqmp = mexchange(NULL, NULL, size, M_PROTO, DL_SET_PHYS_ADDR_REQ);
    189 	if (reqmp == NULL)
    190 		return (ENOMEM);
    191 
    192 	phyaddr = (dl_set_phys_addr_req_t *)reqmp->b_rptr;
    193 	phyaddr->dl_addr_offset = sizeof (dl_set_phys_addr_req_t);
    194 	phyaddr->dl_addr_length = softmac->smac_addrlen;
    195 	(void) memcpy(&phyaddr[1], macaddr, softmac->smac_addrlen);
    196 
    197 	slp = softmac->smac_lower;
    198 	ASSERT(slp != NULL);
    199 	return (softmac_proto_tx(slp, reqmp, NULL));
    200 }
    201 
    202 void
    203 softmac_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
    204 {
    205 	softmac_lower_t *slp = ((softmac_t *)arg)->smac_lower;
    206 	mblk_t *ackmp;
    207 
    208 	ASSERT(slp != NULL);
    209 	softmac_ioctl_tx(slp, mp, &ackmp);
    210 	qreply(wq, ackmp);
    211 }
    212 
    213 static void
    214 softmac_process_notify_ind(softmac_t *softmac, mblk_t *mp)
    215 {
    216 	dl_notify_ind_t	*dlnip = (dl_notify_ind_t *)mp->b_rptr;
    217 	uint_t		addroff, addrlen;
    218 
    219 	ASSERT(dlnip->dl_primitive == DL_NOTIFY_IND);
    220 
    221 	switch (dlnip->dl_notification) {
    222 	case DL_NOTE_PHYS_ADDR:
    223 		if (dlnip->dl_data != DL_CURR_PHYS_ADDR)
    224 			break;
    225 
    226 		addroff = dlnip->dl_addr_offset;
    227 		addrlen = dlnip->dl_addr_length - softmac->smac_saplen;
    228 		if (addroff == 0 || addrlen != softmac->smac_addrlen ||
    229 		    !MBLKIN(mp, addroff, addrlen)) {
    230 			cmn_err(CE_NOTE, "softmac: got malformed "
    231 			    "DL_NOTIFY_IND; length/offset %d/%d",
    232 			    addrlen, addroff);
    233 			break;
    234 		}
    235 
    236 		mac_unicst_update(softmac->smac_mh, mp->b_rptr + addroff);
    237 		break;
    238 
    239 	case DL_NOTE_LINK_UP:
    240 		mac_link_update(softmac->smac_mh, LINK_STATE_UP);
    241 		break;
    242 
    243 	case DL_NOTE_LINK_DOWN:
    244 		mac_link_update(softmac->smac_mh, LINK_STATE_DOWN);
    245 		break;
    246 	}
    247 
    248 	freemsg(mp);
    249 }
    250 
    251 void
    252 softmac_notify_thread(void *arg)
    253 {
    254 	softmac_t	*softmac = arg;
    255 	callb_cpr_t	cprinfo;
    256 
    257 	CALLB_CPR_INIT(&cprinfo, &softmac->smac_mutex, callb_generic_cpr,
    258 	    "softmac_notify_thread");
    259 
    260 	mutex_enter(&softmac->smac_mutex);
    261 
    262 	/*
    263 	 * Quit the thread if smac_mh is unregistered.
    264 	 */
    265 	while (softmac->smac_mh != NULL &&
    266 	    !(softmac->smac_flags & SOFTMAC_NOTIFY_QUIT)) {
    267 		mblk_t		*mp, *nextmp;
    268 
    269 		if ((mp = softmac->smac_notify_head) == NULL) {
    270 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
    271 			cv_wait(&softmac->smac_cv, &softmac->smac_mutex);
    272 			CALLB_CPR_SAFE_END(&cprinfo, &softmac->smac_mutex);
    273 			continue;
    274 		}
    275 
    276 		softmac->smac_notify_head = softmac->smac_notify_tail = NULL;
    277 		mutex_exit(&softmac->smac_mutex);
    278 
    279 		while (mp != NULL) {
    280 			nextmp = mp->b_next;
    281 			mp->b_next = NULL;
    282 			softmac_process_notify_ind(softmac, mp);
    283 			mp = nextmp;
    284 		}
    285 		mutex_enter(&softmac->smac_mutex);
    286 	}
    287 
    288 	/*
    289 	 * The softmac is being destroyed, simply free all of the DL_NOTIFY_IND
    290 	 * messages left in the queue which did not have the chance to be
    291 	 * processed.
    292 	 */
    293 	freemsgchain(softmac->smac_notify_head);
    294 	softmac->smac_notify_head = softmac->smac_notify_tail = NULL;
    295 	softmac->smac_notify_thread = NULL;
    296 	cv_broadcast(&softmac->smac_cv);
    297 	CALLB_CPR_EXIT(&cprinfo);
    298 	thread_exit();
    299 }
    300 
    301 static void
    302 softmac_enqueue_notify_ind(queue_t *rq, mblk_t *mp)
    303 {
    304 	softmac_lower_t	*slp = rq->q_ptr;
    305 	softmac_t	*softmac = slp->sl_softmac;
    306 
    307 	mutex_enter(&softmac->smac_mutex);
    308 	if (softmac->smac_notify_tail == NULL) {
    309 		softmac->smac_notify_head = softmac->smac_notify_tail = mp;
    310 	} else {
    311 		softmac->smac_notify_tail->b_next = mp;
    312 		softmac->smac_notify_tail = mp;
    313 	}
    314 	cv_broadcast(&softmac->smac_cv);
    315 	mutex_exit(&softmac->smac_mutex);
    316 }
    317 
    318 static void
    319 softmac_process_dlpi(softmac_lower_t *slp, mblk_t *mp, uint_t minlen,
    320     t_uscalar_t reqprim)
    321 {
    322 	const char *ackname;
    323 
    324 	ackname = dl_primstr(((union DL_primitives *)mp->b_rptr)->dl_primitive);
    325 
    326 	if (MBLKL(mp) < minlen) {
    327 		cmn_err(CE_WARN, "softmac: got short %s", ackname);
    328 		freemsg(mp);
    329 		return;
    330 	}
    331 
    332 	mutex_enter(&slp->sl_mutex);
    333 	if (slp->sl_pending_prim != reqprim) {
    334 		cmn_err(CE_NOTE, "softmac: got unexpected %s", ackname);
    335 		mutex_exit(&slp->sl_mutex);
    336 		freemsg(mp);
    337 		return;
    338 	}
    339 
    340 	slp->sl_pending_prim = DL_PRIM_INVAL;
    341 	slp->sl_ack_mp = mp;
    342 	cv_signal(&slp->sl_cv);
    343 	mutex_exit(&slp->sl_mutex);
    344 }
    345 
    346 void
    347 softmac_rput_process_proto(queue_t *rq, mblk_t *mp)
    348 {
    349 	softmac_lower_t		*slp = rq->q_ptr;
    350 	union DL_primitives	*dlp = (union DL_primitives *)mp->b_rptr;
    351 	ssize_t			len = MBLKL(mp);
    352 	const char		*primstr;
    353 
    354 	if (len < sizeof (t_uscalar_t)) {
    355 		cmn_err(CE_WARN, "softmac: got runt DLPI message");
    356 		goto exit;
    357 	}
    358 
    359 	primstr = dl_primstr(dlp->dl_primitive);
    360 
    361 	switch (dlp->dl_primitive) {
    362 	case DL_OK_ACK:
    363 		if (len < DL_OK_ACK_SIZE)
    364 			goto runt;
    365 
    366 		softmac_process_dlpi(slp, mp, DL_OK_ACK_SIZE,
    367 		    dlp->ok_ack.dl_correct_primitive);
    368 		return;
    369 
    370 	case DL_ERROR_ACK:
    371 		if (len < DL_ERROR_ACK_SIZE)
    372 			goto runt;
    373 
    374 		softmac_process_dlpi(slp, mp, DL_ERROR_ACK_SIZE,
    375 		    dlp->error_ack.dl_error_primitive);
    376 		return;
    377 
    378 	case DL_NOTIFY_IND:
    379 		if (len < DL_NOTIFY_IND_SIZE)
    380 			goto runt;
    381 
    382 		/*
    383 		 * Enqueue all the DL_NOTIFY_IND messages and process them
    384 		 * in another separate thread to avoid deadlock. Here is an
    385 		 * example of the deadlock scenario:
    386 		 *
    387 		 * Thread A: mac_promisc_set()->softmac_m_promisc()
    388 		 *
    389 		 *   The softmac driver waits for the ACK of the
    390 		 *   DL_PROMISC_PHYS request with the MAC perimeter;
    391 		 *
    392 		 * Thread B:
    393 		 *
    394 		 *   The driver handles the DL_PROMISC_PHYS request. Before
    395 		 *   it sends back the ACK, it could first send a
    396 		 *   DL_NOTE_PROMISC_ON_PHYS notification.
    397 		 *
    398 		 * Since DL_NOTIFY_IND could eventually cause softmac to call
    399 		 * mac_xxx_update(), which requires MAC perimeter, this would
    400 		 * cause deadlock between the two threads. Enqueuing the
    401 		 * DL_NOTIFY_IND message and defer its processing would
    402 		 * avoid the potential deadlock.
    403 		 */
    404 		softmac_enqueue_notify_ind(rq, mp);
    405 		return;
    406 
    407 	case DL_NOTIFY_ACK:
    408 		softmac_process_dlpi(slp, mp, DL_NOTIFY_ACK_SIZE,
    409 		    DL_NOTIFY_REQ);
    410 		return;
    411 
    412 	case DL_CAPABILITY_ACK:
    413 		softmac_process_dlpi(slp, mp, DL_CAPABILITY_ACK_SIZE,
    414 		    DL_CAPABILITY_REQ);
    415 		return;
    416 
    417 	case DL_BIND_ACK:
    418 		softmac_process_dlpi(slp, mp, DL_BIND_ACK_SIZE, DL_BIND_REQ);
    419 		return;
    420 
    421 	case DL_CONTROL_ACK:
    422 		softmac_process_dlpi(slp, mp, DL_CONTROL_ACK_SIZE,
    423 		    DL_CONTROL_REQ);
    424 		return;
    425 
    426 	case DL_UNITDATA_IND:
    427 	case DL_PHYS_ADDR_ACK:
    428 		/*
    429 		 * a. Because the stream is in DLIOCRAW mode,
    430 		 *    DL_UNITDATA_IND messages are not expected.
    431 		 * b. The lower stream should not receive DL_PHYS_ADDR_REQ,
    432 		 *    so DL_PHYS_ADDR_ACK messages are also unexpected.
    433 		 */
    434 	default:
    435 		cmn_err(CE_WARN, "softmac: got unexpected %s", primstr);
    436 		break;
    437 	}
    438 exit:
    439 	freemsg(mp);
    440 	return;
    441 runt:
    442 	cmn_err(CE_WARN, "softmac: got runt %s", primstr);
    443 	freemsg(mp);
    444 }
    445 
    446 void
    447 softmac_rput_process_notdata(queue_t *rq, softmac_upper_t *sup, mblk_t *mp)
    448 {
    449 	softmac_lower_t		*slp = rq->q_ptr;
    450 	union DL_primitives	*dlp;
    451 	ssize_t			len = MBLKL(mp);
    452 
    453 	switch (DB_TYPE(mp)) {
    454 	case M_PROTO:
    455 	case M_PCPROTO:
    456 		/*
    457 		 * If this is a shared-lower-stream, pass it to softmac to
    458 		 * process.
    459 		 */
    460 		if (sup == NULL) {
    461 			softmac_rput_process_proto(rq, mp);
    462 			break;
    463 		}
    464 
    465 		/*
    466 		 * Dedicated-lower-stream.
    467 		 */
    468 		dlp = (union DL_primitives *)mp->b_rptr;
    469 		ASSERT(len >= sizeof (dlp->dl_primitive));
    470 		switch (dlp->dl_primitive) {
    471 		case DL_OK_ACK:
    472 			if (len < DL_OK_ACK_SIZE)
    473 				goto runt;
    474 
    475 			/*
    476 			 * If this is a DL_OK_ACK for a DL_UNBIND_REQ, pass it
    477 			 * to softmac to process, otherwise directly pass it to
    478 			 * the upper stream.
    479 			 */
    480 			if (dlp->ok_ack.dl_correct_primitive == DL_UNBIND_REQ) {
    481 				softmac_rput_process_proto(rq, mp);
    482 				break;
    483 			}
    484 
    485 			putnext(sup->su_rq, mp);
    486 			break;
    487 		case DL_ERROR_ACK:
    488 			if (len < DL_ERROR_ACK_SIZE)
    489 				goto runt;
    490 
    491 			/*
    492 			 * If this is a DL_ERROR_ACK for a DL_UNBIND_REQ, pass
    493 			 * it to softmac to process, otherwise directly pass it
    494 			 * to the upper stream.
    495 			 */
    496 			if (dlp->error_ack.dl_error_primitive ==
    497 			    DL_UNBIND_REQ) {
    498 				softmac_rput_process_proto(rq, mp);
    499 				break;
    500 			}
    501 
    502 			putnext(sup->su_rq, mp);
    503 			break;
    504 		case DL_BIND_ACK:
    505 		case DL_CAPABILITY_ACK:
    506 			softmac_rput_process_proto(rq, mp);
    507 			break;
    508 		default:
    509 			putnext(sup->su_rq, mp);
    510 			break;
    511 		}
    512 		break;
    513 	case M_FLUSH:
    514 		if (*mp->b_rptr & FLUSHR)
    515 			flushq(rq, FLUSHDATA);
    516 		if (*mp->b_rptr & FLUSHW)
    517 			flushq(OTHERQ(rq), FLUSHDATA);
    518 		putnext(rq, mp);
    519 		break;
    520 
    521 	case M_IOCACK:
    522 	case M_IOCNAK:
    523 	case M_COPYIN:
    524 	case M_COPYOUT:
    525 		if (sup != NULL) {
    526 			putnext(sup->su_rq, mp);
    527 			break;
    528 		}
    529 
    530 		mutex_enter(&slp->sl_mutex);
    531 		if (!slp->sl_pending_ioctl) {
    532 			mutex_exit(&slp->sl_mutex);
    533 			cmn_err(CE_NOTE, "softmac: got unexpected mblk "
    534 			    "type 0x%x", DB_TYPE(mp));
    535 			freemsg(mp);
    536 			break;
    537 		}
    538 
    539 		slp->sl_pending_ioctl = B_FALSE;
    540 		slp->sl_ack_mp = mp;
    541 		cv_broadcast(&slp->sl_cv);
    542 		mutex_exit(&slp->sl_mutex);
    543 		break;
    544 
    545 	default:
    546 		cmn_err(CE_NOTE, "softmac: got unsupported mblk type 0x%x",
    547 		    DB_TYPE(mp));
    548 		freemsg(mp);
    549 		break;
    550 	}
    551 	return;
    552 runt:
    553 	cmn_err(CE_WARN, "softmac: got runt %s", dl_primstr(dlp->dl_primitive));
    554 	freemsg(mp);
    555 }
    556