xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c (revision 8fd04b8338ed5093ec2d1e668fa620b7de44c177)
1 /*
2  * Copyright (c) 2000-2001 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * $Id: smb_trantcp.c,v 1.39 2005/03/02 01:27:44 lindak Exp $
33  */
34 /*
35  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
36  * Use is subject to license terms.
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/autoconf.h>
42 #include <sys/sysmacros.h>
43 #include <sys/sunddi.h>
44 #include <sys/kmem.h>
45 #include <sys/proc.h>
46 #include <sys/protosw.h>
47 #include <sys/socket.h>
48 #include <sys/poll.h>
49 #include <sys/stream.h>
50 #include <sys/strsubr.h>
51 #include <sys/strsun.h>
52 #include <sys/stropts.h>
53 #include <sys/cmn_err.h>
54 #include <sys/tihdr.h>
55 #include <sys/tiuser.h>
56 #include <sys/t_kuser.h>
57 #include <sys/priv.h>
58 
59 #include <net/if.h>
60 #include <net/route.h>
61 
62 #include <netinet/in.h>
63 #include <netinet/tcp.h>
64 
65 #include <netsmb/smb_osdep.h>
66 #include <netsmb/mchain.h>
67 #include <netsmb/netbios.h>
68 
69 #include <netsmb/smb.h>
70 #include <netsmb/smb_conn.h>
71 #include <netsmb/smb_subr.h>
72 #include <netsmb/smb_tran.h>
73 #include <netsmb/smb_trantcp.h>
74 
75 /*
76  * SMB messages are up to 64K.
77  * Let's leave room for two.
78  */
79 static int smb_tcpsndbuf = 0x20000;
80 static int smb_tcprcvbuf = 0x20000;
81 
82 static int  nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp,
83 	uint8_t *rpcodep);
84 static int  nb_disconnect(struct nbpcb *nbp);
85 
86 
87 /*
88  * Get mblks into *mpp until the data length is at least mlen.
89  * Note that *mpp may already contain a fragment.
90  *
91  * If we ever have to wait more than 15 sec. to read a message,
92  * return ETIME.  (Caller will declare the VD dead.)
93  */
94 static int
95 nb_getmsg_mlen(struct nbpcb *nbp, mblk_t **mpp, size_t mlen)
96 {
97 	mblk_t *im, *tm;
98 	union T_primitives	*pptr;
99 	size_t dlen;
100 	int events, fmode, timo, waitflg;
101 	int error = 0;
102 
103 	/*
104 	 * Get the first message (fragment) if
105 	 * we don't already have a left-over.
106 	 */
107 	dlen = msgdsize(*mpp); /* *mpp==null is OK */
108 	while (dlen < mlen) {
109 
110 		/*
111 		 * I think we still want this to return ETIME
112 		 * if nothing arrives for SMB_NBTIMO (15) sec.
113 		 * so we can report "server not responding".
114 		 * We _could_ just block here now that our
115 		 * IOD is just a reader.
116 		 */
117 #if 1
118 		/* Wait with timeout... */
119 		events = 0;
120 		waitflg = READWAIT;
121 		timo = SEC_TO_TICK(SMB_NBTIMO);
122 		error = t_kspoll(nbp->nbp_tiptr, timo, waitflg, &events);
123 		if (!error && !events)
124 			error = ETIME;
125 		if (error)
126 			break;
127 		/* file mode for recv is: */
128 		fmode = FNDELAY; /* non-blocking */
129 #else
130 		fmode = 0; /* normal (blocking) */
131 #endif
132 
133 		/* Get some more... */
134 		tm = NULL;
135 		error = tli_recv(nbp->nbp_tiptr, &tm, fmode);
136 		if (error == EAGAIN)
137 			continue;
138 		if (error)
139 			break;
140 
141 		/*
142 		 * Normally get M_DATA messages here,
143 		 * but have to check for other types.
144 		 */
145 		switch (tm->b_datap->db_type) {
146 		case M_DATA:
147 			break;
148 		case M_PROTO:
149 		case M_PCPROTO:
150 			/*LINTED*/
151 			pptr = (union T_primitives *)tm->b_rptr;
152 			switch (pptr->type) {
153 			case T_DATA_IND:
154 				/* remove 1st mblk, keep the rest. */
155 				im = tm->b_cont;
156 				tm->b_cont = NULL;
157 				freeb(tm);
158 				tm = im;
159 				break;
160 			case T_DISCON_IND:
161 				/* Peer disconnected. */
162 				NBDEBUG("T_DISCON_IND: reason=%d",
163 				    pptr->discon_ind.DISCON_reason);
164 				goto discon;
165 			case T_ORDREL_IND:
166 				/* Peer disconnecting. */
167 				NBDEBUG("T_ORDREL_IND");
168 				goto discon;
169 			case T_OK_ACK:
170 				switch (pptr->ok_ack.CORRECT_prim) {
171 				case T_DISCON_REQ:
172 					NBDEBUG("T_OK_ACK/T_DISCON_REQ");
173 					goto discon;
174 				default:
175 					NBDEBUG("T_OK_ACK/prim=%d",
176 					    pptr->ok_ack.CORRECT_prim);
177 					goto discon;
178 				}
179 			default:
180 				NBDEBUG("M_PROTO/type=%d", pptr->type);
181 				goto discon;
182 			}
183 			break; /* M_PROTO, M_PCPROTO */
184 
185 		default:
186 			NBDEBUG("unexpected msg type=%d",
187 			    tm->b_datap->db_type);
188 			/*FALLTHROUGH*/
189 discon:
190 			/*
191 			 * The connection is no longer usable.
192 			 * Drop this message and disconnect.
193 			 *
194 			 * Note: nb_disconnect only does t_snddis
195 			 * on the first call, but does important
196 			 * cleanup and state change on any call.
197 			 */
198 			freemsg(tm);
199 			(void) nb_disconnect(nbp);
200 			return (ENOTCONN);
201 		}
202 
203 		/*
204 		 * If we have a data message, append it to
205 		 * the previous chunk(s) and update dlen
206 		 */
207 		if (!tm)
208 			continue;
209 		if (*mpp == NULL) {
210 			*mpp = tm;
211 		} else {
212 			/* Append */
213 			for (im = *mpp; im->b_cont; im = im->b_cont)
214 				;
215 			im->b_cont = tm;
216 		}
217 		dlen += msgdsize(tm);
218 	}
219 
220 	return (error);
221 }
222 
223 /*
224  * Send a T_DISCON_REQ (disconnect)
225  */
226 static int
227 nb_snddis(TIUSER *tiptr)
228 {
229 	cred_t *cr;
230 	mblk_t *mp;
231 	struct T_discon_req *dreq;
232 	int error, fmode, mlen;
233 
234 	cr = ddi_get_cred();
235 	mlen = sizeof (struct T_discon_req);
236 	if (!(mp = allocb_cred_wait(mlen, STR_NOSIG, &error, cr, NOPID)))
237 		return (error);
238 
239 	mp->b_datap->db_type = M_PROTO;
240 	/*LINTED*/
241 	dreq = (struct T_discon_req *)mp->b_wptr;
242 	dreq->PRIM_type = T_DISCON_REQ;
243 	dreq->SEQ_number = -1;
244 	mp->b_wptr += sizeof (struct T_discon_req);
245 
246 	fmode = tiptr->fp->f_flag;
247 	if ((error = tli_send(tiptr, mp, fmode)) != 0)
248 		return (error);
249 
250 	fmode = 0; /* need to block */
251 	error = get_ok_ack(tiptr, T_DISCON_REQ, fmode);
252 
253 	return (error);
254 }
255 
256 /*
257  * Stuff the NetBIOS header into space already prepended.
258  */
259 static void
260 nb_sethdr(mblk_t *m, uint8_t type, uint32_t len)
261 {
262 	uint32_t *p;
263 
264 	len &= 0x1FFFF;
265 	len |= (type << 24);
266 
267 	/*LINTED*/
268 	p = (uint32_t *)m->b_rptr;
269 	*p = htonl(len);
270 }
271 
272 /*
273  * Wait for up to 15 sec. for the next packet.
274  * Often return ETIME and do nothing else.
275  * When a packet header is available, check
276  * the header and get the length, but don't
277  * consume it.  No side effects here except
278  * for the pullupmsg call.
279  */
280 static int
281 nbssn_peekhdr(struct nbpcb *nbp, size_t *lenp,	uint8_t *rpcodep)
282 {
283 	uint32_t len, *hdr;
284 	int error;
285 
286 	/*
287 	 * Get the first message (fragment) if
288 	 * we don't already have a left-over.
289 	 */
290 	error = nb_getmsg_mlen(nbp, &nbp->nbp_frag, sizeof (len));
291 	if (error)
292 		return (error);
293 
294 	if (!pullupmsg(nbp->nbp_frag, sizeof (len)))
295 		return (ENOSR);
296 
297 	/*
298 	 * Check the NetBIOS header.
299 	 * (NOT consumed here)
300 	 */
301 	/*LINTED*/
302 	hdr = (uint32_t *)nbp->nbp_frag->b_rptr;
303 
304 	len = ntohl(*hdr);
305 	if ((len >> 16) & 0xFE) {
306 		NBDEBUG("bad nb header received 0x%x (MBZ flag set)\n", len);
307 		return (EPIPE);
308 	}
309 	*rpcodep = (len >> 24) & 0xFF;
310 	switch (*rpcodep) {
311 	case NB_SSN_MESSAGE:
312 	case NB_SSN_REQUEST:
313 	case NB_SSN_POSRESP:
314 	case NB_SSN_NEGRESP:
315 	case NB_SSN_RTGRESP:
316 	case NB_SSN_KEEPALIVE:
317 		break;
318 	default:
319 		NBDEBUG("bad nb header received 0x%x (bogus type)\n", len);
320 		return (EPIPE);
321 	}
322 	len &= 0x1ffff;
323 	if (len > NB_MAXPKTLEN) {
324 		NBDEBUG("packet too long (%d)\n", len);
325 		return (EFBIG);
326 	}
327 	*lenp = len;
328 	return (0);
329 }
330 
331 /*
332  * Receive a NetBIOS message.  This may block to wait for the entire
333  * message to arrive.  The caller knows there is (or should be) a
334  * message to be read.  When we receive and drop a keepalive or
335  * zero-length message, return EAGAIN so the caller knows that
336  * something was received.  This avoids false triggering of the
337  * "server not responding" state machine.
338  */
339 /*ARGSUSED*/
340 static int
341 nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp,
342     uint8_t *rpcodep)
343 {
344 	TIUSER *tiptr = nbp->nbp_tiptr;
345 	mblk_t *m0;
346 	uint8_t rpcode;
347 	int error;
348 	size_t rlen, len;
349 
350 	/* We should be the only reader. */
351 	ASSERT(nbp->nbp_flags & NBF_RECVLOCK);
352 	if ((nbp->nbp_flags & NBF_CONNECTED) == 0)
353 		return (ENOTCONN);
354 
355 	if (tiptr == NULL)
356 		return (EBADF);
357 	if (mpp) {
358 		if (*mpp) {
359 			NBDEBUG("*mpp not 0 - leak?");
360 		}
361 		*mpp = NULL;
362 	}
363 	m0 = NULL;
364 
365 	/*
366 	 * Get the NetBIOS header (not consumed yet)
367 	 */
368 	error = nbssn_peekhdr(nbp, &len, &rpcode);
369 	if (error) {
370 		if (error != ETIME)
371 			NBDEBUG("peekhdr, error=%d\n", error);
372 		return (error);
373 	}
374 	NBDEBUG("Have pkt, type=0x%x len=0x%x\n",
375 	    (int)rpcode, (int)len);
376 
377 	/*
378 	 * Block here waiting for the whole packet to arrive.
379 	 * If we get a timeout, return without side effects.
380 	 * The data length we wait for here includes both the
381 	 * NetBIOS header and the payload.
382 	 */
383 	error = nb_getmsg_mlen(nbp, &nbp->nbp_frag, len + 4);
384 	if (error) {
385 		NBDEBUG("getmsg(body), error=%d\n", error);
386 		return (error);
387 	}
388 
389 	/*
390 	 * We now have an entire NetBIOS message.
391 	 * Trim off the NetBIOS header and consume it.
392 	 * Note: _peekhdr has done pullupmsg for us,
393 	 * so we know it's safe to advance b_rptr.
394 	 */
395 	m0 = nbp->nbp_frag;
396 	m0->b_rptr += 4;
397 
398 	/*
399 	 * There may be more data after the message
400 	 * we're about to return, in which case we
401 	 * split it and leave the remainder.
402 	 */
403 	rlen = msgdsize(m0);
404 	ASSERT(rlen >= len);
405 	nbp->nbp_frag = NULL;
406 	if (rlen > len)
407 		nbp->nbp_frag = m_split(m0, len, 1);
408 
409 	if (nbp->nbp_state != NBST_SESSION) {
410 		/*
411 		 * No session is established.
412 		 * Return whatever packet we got.
413 		 */
414 		goto out;
415 	}
416 
417 	/*
418 	 * A session is established; the only packets
419 	 * we should see are session message and
420 	 * keep-alive packets.  Drop anything else.
421 	 */
422 	switch (rpcode) {
423 
424 	case NB_SSN_KEEPALIVE:
425 		/*
426 		 * It's a keepalive.  Discard any data in it
427 		 * (there's not supposed to be any, but that
428 		 * doesn't mean some server won't send some)
429 		 */
430 		if (len)
431 			NBDEBUG("Keepalive with data %d\n", (int)len);
432 		error = EAGAIN;
433 		break;
434 
435 	case NB_SSN_MESSAGE:
436 		/*
437 		 * Session message.  Does it have any data?
438 		 */
439 		if (len == 0) {
440 			/*
441 			 * No data - treat as keepalive (drop).
442 			 */
443 			error = EAGAIN;
444 			break;
445 		}
446 		/*
447 		 * Yes, has data.  Return it.
448 		 */
449 		error = 0;
450 		break;
451 
452 	default:
453 		/*
454 		 * Drop anything else.
455 		 */
456 		NBDEBUG("non-session packet %x\n", rpcode);
457 		error = EAGAIN;
458 		break;
459 	}
460 
461 out:
462 	if (error) {
463 		if (m0)
464 			m_freem(m0);
465 		return (error);
466 	}
467 	if (mpp)
468 		*mpp = m0;
469 	else
470 		m_freem(m0);
471 	*lenp = (int)len;
472 	*rpcodep = rpcode;
473 	return (0);
474 }
475 
476 /*
477  * SMB transport interface
478  */
479 /*ARGSUSED*/
480 static int
481 smb_nbst_create(struct smb_vc *vcp, cred_t *cr)
482 {
483 	struct nbpcb *nbp;
484 
485 	nbp = kmem_zalloc(sizeof (struct nbpcb), KM_SLEEP);
486 
487 	nbp->nbp_timo.tv_sec = SMB_NBTIMO;
488 	nbp->nbp_state = NBST_CLOSED; /* really IDLE */
489 	nbp->nbp_vc = vcp;
490 	nbp->nbp_sndbuf = smb_tcpsndbuf;
491 	nbp->nbp_rcvbuf = smb_tcprcvbuf;
492 	mutex_init(&nbp->nbp_lock, NULL, MUTEX_DRIVER, NULL);
493 	vcp->vc_tdata = nbp;
494 
495 	return (0);
496 }
497 
498 /*ARGSUSED*/
499 static int
500 smb_nbst_done(struct smb_vc *vcp)
501 {
502 	struct nbpcb *nbp = vcp->vc_tdata;
503 
504 	if (nbp == NULL)
505 		return (ENOTCONN);
506 	vcp->vc_tdata = NULL;
507 
508 	/*
509 	 * Don't really need to disconnect here,
510 	 * because the close following will do it.
511 	 * But it's harmless.
512 	 */
513 	if (nbp->nbp_flags & NBF_CONNECTED)
514 		(void) nb_disconnect(nbp);
515 	if (nbp->nbp_tiptr)
516 		(void) t_kclose(nbp->nbp_tiptr, 0);
517 	if (nbp->nbp_laddr)
518 		smb_free_sockaddr((struct sockaddr *)nbp->nbp_laddr);
519 	if (nbp->nbp_paddr)
520 		smb_free_sockaddr((struct sockaddr *)nbp->nbp_paddr);
521 	mutex_destroy(&nbp->nbp_lock);
522 	kmem_free(nbp, sizeof (*nbp));
523 	return (0);
524 }
525 
526 static int
527 smb_nbst_loan_fp(struct smb_vc *vcp, struct file *fp, cred_t *cr)
528 {
529 	struct nbpcb *nbp = vcp->vc_tdata;
530 	TIUSER *tiptr;
531 	int error = 0;
532 
533 	mutex_enter(&nbp->nbp_lock);
534 
535 	/*
536 	 * Un-loan the existing one, if any.
537 	 */
538 	if (nbp->nbp_tiptr != NULL) {
539 		(void) t_kclose(nbp->nbp_tiptr, 0);
540 		nbp->nbp_tiptr = NULL;
541 		nbp->nbp_flags &= ~NBF_CONNECTED;
542 		nbp->nbp_state = NBST_CLOSED;
543 	}
544 
545 	/*
546 	 * Loan the new one passed in.
547 	 */
548 	if (fp != NULL && 0 == (error =
549 	    t_kopen(fp, 0, 0, &tiptr, cr))) {
550 		nbp->nbp_tiptr = tiptr;
551 		nbp->nbp_fmode = tiptr->fp->f_flag;
552 		nbp->nbp_flags |= NBF_CONNECTED;
553 		nbp->nbp_state = NBST_SESSION;
554 	}
555 
556 	mutex_exit(&nbp->nbp_lock);
557 
558 	return (error);
559 }
560 
561 /*ARGSUSED*/
562 static int
563 smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap)
564 {
565 	return (ENOTSUP);
566 }
567 
568 /*ARGSUSED*/
569 static int
570 smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap)
571 {
572 	return (ENOTSUP);
573 }
574 
575 /*ARGSUSED*/
576 static int
577 smb_nbst_disconnect(struct smb_vc *vcp)
578 {
579 	struct nbpcb *nbp = vcp->vc_tdata;
580 
581 	if (nbp == NULL)
582 		return (ENOTCONN);
583 
584 	return (nb_disconnect(nbp));
585 }
586 
587 static int
588 nb_disconnect(struct nbpcb *nbp)
589 {
590 	TIUSER *tiptr;
591 	int save_flags;
592 	int err = 0;
593 
594 	tiptr = nbp->nbp_tiptr;
595 	if (tiptr == NULL)
596 		return (EBADF);
597 
598 	mutex_enter(&nbp->nbp_lock);
599 	save_flags = nbp->nbp_flags;
600 	nbp->nbp_flags &= ~NBF_CONNECTED;
601 	if (nbp->nbp_frag) {
602 		freemsg(nbp->nbp_frag);
603 		nbp->nbp_frag = NULL;
604 	}
605 	mutex_exit(&nbp->nbp_lock);
606 
607 	if (save_flags & NBF_CONNECTED)
608 		err = nb_snddis(tiptr);
609 
610 	if (nbp->nbp_state != NBST_RETARGET) {
611 		nbp->nbp_state = NBST_CLOSED;
612 	}
613 
614 	return (err);
615 }
616 
617 /*
618  * Always consume the message.
619  * (On error too!)
620  */
621 /*ARGSUSED*/
622 static int
623 smb_nbst_send(struct smb_vc *vcp, mblk_t *m)
624 {
625 	struct nbpcb *nbp = vcp->vc_tdata;
626 	ptrdiff_t diff;
627 	uint32_t mlen;
628 	int error;
629 
630 	if (nbp == NULL || nbp->nbp_tiptr == NULL) {
631 		error = EBADF;
632 		goto errout;
633 	}
634 
635 	/*
636 	 * Get the message length, which
637 	 * does NOT include the NetBIOS header
638 	 */
639 	mlen = msgdsize(m);
640 
641 	/*
642 	 * Normally, mb_init() will have left space
643 	 * for us to prepend the NetBIOS header in
644 	 * the data block of the first mblk.
645 	 * However, we have to check in case other
646 	 * code did not leave this space, or if the
647 	 * message is from dupmsg (db_ref > 1)
648 	 *
649 	 * If don't find room in the first data block,
650 	 * we have to allocb a new message and link it
651 	 * on the front of the chain.  We try not to
652 	 * do this becuase it's less efficient.  Also,
653 	 * some network drivers will apparently send
654 	 * each mblk in the chain as separate frames.
655 	 * (That's arguably a driver bug.)
656 	 *
657 	 * Not bothering with allocb_cred_wait below
658 	 * because the message we're prepending to
659 	 * should already have a db_credp.
660 	 */
661 
662 	diff = MBLKHEAD(m);
663 	if (diff == 4 && DB_REF(m) == 1) {
664 		/* We can use the first dblk. */
665 		m->b_rptr -= 4;
666 	} else {
667 		/* Link a new mblk on the head. */
668 		mblk_t *m0;
669 
670 		/* M_PREPEND */
671 		m0 = allocb_wait(4, BPRI_LO, STR_NOSIG, &error);
672 		if (!m0)
673 			goto errout;
674 
675 		m0->b_wptr += 4;
676 		m0->b_cont = m;
677 		m = m0;
678 	}
679 
680 	nb_sethdr(m, NB_SSN_MESSAGE, mlen);
681 	error = tli_send(nbp->nbp_tiptr, m, 0);
682 	return (error);
683 
684 errout:
685 	if (m)
686 		m_freem(m);
687 	return (error);
688 }
689 
690 static int
691 smb_nbst_recv(struct smb_vc *vcp, mblk_t **mpp)
692 {
693 	struct nbpcb *nbp = vcp->vc_tdata;
694 	uint8_t rpcode;
695 	int error, rplen;
696 
697 	mutex_enter(&nbp->nbp_lock);
698 	if (nbp->nbp_flags & NBF_RECVLOCK) {
699 		NBDEBUG("attempt to reenter session layer!\n");
700 		mutex_exit(&nbp->nbp_lock);
701 		return (EWOULDBLOCK);
702 	}
703 	nbp->nbp_flags |= NBF_RECVLOCK;
704 	mutex_exit(&nbp->nbp_lock);
705 	error = nbssn_recv(nbp, mpp, &rplen, &rpcode);
706 	mutex_enter(&nbp->nbp_lock);
707 	nbp->nbp_flags &= ~NBF_RECVLOCK;
708 	mutex_exit(&nbp->nbp_lock);
709 	return (error);
710 }
711 
712 /*
713  * Wait for up to "ticks" clock ticks for input on vcp.
714  * Returns zero if input is available, otherwise ETIME
715  * indicating time expired, or other error codes.
716  */
717 /*ARGSUSED*/
718 static int
719 smb_nbst_poll(struct smb_vc *vcp, int ticks)
720 {
721 	int error;
722 	int events = 0;
723 	int waitflg = READWAIT;
724 	struct nbpcb *nbp = vcp->vc_tdata;
725 
726 	error = t_kspoll(nbp->nbp_tiptr, ticks, waitflg, &events);
727 	if (!error && !events)
728 		error = ETIME;
729 
730 	return (error);
731 }
732 
733 static int
734 smb_nbst_getparam(struct smb_vc *vcp, int param, void *data)
735 {
736 	struct nbpcb *nbp = vcp->vc_tdata;
737 
738 	switch (param) {
739 	case SMBTP_SNDSZ:
740 		*(int *)data = nbp->nbp_sndbuf;
741 		break;
742 	case SMBTP_RCVSZ:
743 		*(int *)data = nbp->nbp_rcvbuf;
744 		break;
745 	case SMBTP_TIMEOUT:
746 		*(struct timespec *)data = nbp->nbp_timo;
747 		break;
748 #ifdef SMBTP_SELECTID
749 	case SMBTP_SELECTID:
750 		*(void **)data = nbp->nbp_selectid;
751 		break;
752 #endif
753 #ifdef SMBTP_UPCALL
754 	case SMBTP_UPCALL:
755 		*(void **)data = nbp->nbp_upcall;
756 		break;
757 #endif
758 	default:
759 		return (EINVAL);
760 	}
761 	return (0);
762 }
763 
764 /*ARGSUSED*/
765 static int
766 smb_nbst_setparam(struct smb_vc *vcp, int param, void *data)
767 {
768 	return (EINVAL);
769 }
770 
771 /*
772  * Check for fatal errors
773  */
774 /*ARGSUSED*/
775 static int
776 smb_nbst_fatal(struct smb_vc *vcp, int error)
777 {
778 	switch (error) {
779 	case ENOTCONN:
780 	case ENETRESET:
781 	case ECONNABORTED:
782 	case EPIPE:
783 		return (1);
784 	}
785 	return (0);
786 }
787 
788 
789 struct smb_tran_desc smb_tran_nbtcp_desc = {
790 	SMBT_NBTCP,
791 	smb_nbst_create,
792 	smb_nbst_done,
793 	smb_nbst_bind,
794 	smb_nbst_connect,
795 	smb_nbst_disconnect,
796 	smb_nbst_send,
797 	smb_nbst_recv,
798 	smb_nbst_poll,
799 	smb_nbst_loan_fp,
800 	smb_nbst_getparam,
801 	smb_nbst_setparam,
802 	smb_nbst_fatal,
803 	{NULL, NULL}
804 };
805