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
softmac_send_notify_req(softmac_lower_t * slp,uint32_t notifications)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
softmac_send_bind_req(softmac_lower_t * slp,uint_t sap)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
softmac_send_unbind_req(softmac_lower_t * slp)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
softmac_send_promisc_req(softmac_lower_t * slp,t_uscalar_t level,boolean_t on)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
softmac_m_promisc(void * arg,boolean_t on)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
softmac_m_multicst(void * arg,boolean_t add,const uint8_t * mca)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
softmac_m_unicst(void * arg,const uint8_t * macaddr)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
softmac_m_ioctl(void * arg,queue_t * wq,mblk_t * mp)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
softmac_process_notify_ind(softmac_t * softmac,mblk_t * mp)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
softmac_notify_thread(void * arg)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
softmac_enqueue_notify_ind(queue_t * rq,mblk_t * mp)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
softmac_process_dlpi(softmac_lower_t * slp,mblk_t * mp,uint_t minlen,t_uscalar_t reqprim)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
softmac_rput_process_proto(queue_t * rq,mblk_t * mp)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
softmac_rput_process_notdata(queue_t * rq,softmac_upper_t * sup,mblk_t * mp)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