xref: /illumos-gate/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c (revision aa693e996c2928c92cccd8a3efe91373e85a6967)
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  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
39  */
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/autoconf.h>
44 #include <sys/sysmacros.h>
45 #include <sys/sunddi.h>
46 #include <sys/kmem.h>
47 #include <sys/proc.h>
48 #include <sys/protosw.h>
49 #include <sys/socket.h>
50 #include <sys/poll.h>
51 #include <sys/stream.h>
52 #include <sys/strsubr.h>
53 #include <sys/strsun.h>
54 #include <sys/stropts.h>
55 #include <sys/cmn_err.h>
56 #include <sys/tihdr.h>
57 #include <sys/tiuser.h>
58 #include <sys/t_kuser.h>
59 #include <sys/priv.h>
60 
61 #include <net/if.h>
62 #include <net/route.h>
63 
64 #include <netinet/in.h>
65 #include <netinet/tcp.h>
66 
67 #include <netsmb/smb_osdep.h>
68 #include <netsmb/mchain.h>
69 #include <netsmb/netbios.h>
70 
71 #include <netsmb/smb.h>
72 #include <netsmb/smb_conn.h>
73 #include <netsmb/smb_subr.h>
74 #include <netsmb/smb_tran.h>
75 #include <netsmb/smb_trantcp.h>
76 
77 static int  nb_disconnect(struct nbpcb *nbp);
78 
79 
80 /*
81  * Get mblks into *mpp until the data length is at least mlen.
82  * Note that *mpp may already contain a fragment.
83  *
84  * If we ever have to wait more than 15 sec. to read a message,
85  * return ETIME.  (Caller will declare the VD dead.)
86  */
87 static int
88 nb_getmsg_mlen(struct nbpcb *nbp, mblk_t **mpp, size_t mlen)
89 {
90 	mblk_t *im, *tm;
91 	union T_primitives	*pptr;
92 	size_t dlen;
93 	int events, fmode, timo, waitflg;
94 	int error = 0;
95 
96 	/* We should be the only reader. */
97 	ASSERT(nbp->nbp_flags & NBF_RECVLOCK);
98 	/* nbp->nbp_tiptr checked by caller */
99 
100 	/*
101 	 * Get the first message (fragment) if
102 	 * we don't already have a left-over.
103 	 */
104 	dlen = msgdsize(*mpp); /* *mpp==null is OK */
105 	while (dlen < mlen) {
106 
107 		/*
108 		 * I think we still want this to return ETIME
109 		 * if nothing arrives for SMB_NBTIMO (15) sec.
110 		 * so we can report "server not responding".
111 		 * We _could_ just block here now that our
112 		 * IOD is just a reader.
113 		 */
114 #if 1
115 		/* Wait with timeout... */
116 		events = 0;
117 		waitflg = READWAIT;
118 		timo = SEC_TO_TICK(SMB_NBTIMO);
119 		error = t_kspoll(nbp->nbp_tiptr, timo, waitflg, &events);
120 		if (!error && !events)
121 			error = ETIME;
122 		if (error)
123 			break;
124 		/* file mode for recv is: */
125 		fmode = FNDELAY; /* non-blocking */
126 #else
127 		fmode = 0; /* normal (blocking) */
128 #endif
129 
130 		/* Get some more... */
131 		tm = NULL;
132 		error = tli_recv(nbp->nbp_tiptr, &tm, fmode);
133 		if (error == EAGAIN)
134 			continue;
135 		if (error)
136 			break;
137 
138 		/*
139 		 * Normally get M_DATA messages here,
140 		 * but have to check for other types.
141 		 */
142 		switch (tm->b_datap->db_type) {
143 		case M_DATA:
144 			break;
145 		case M_PROTO:
146 		case M_PCPROTO:
147 			/*LINTED*/
148 			pptr = (union T_primitives *)tm->b_rptr;
149 			switch (pptr->type) {
150 			case T_DATA_IND:
151 				/* remove 1st mblk, keep the rest. */
152 				im = tm->b_cont;
153 				tm->b_cont = NULL;
154 				freeb(tm);
155 				tm = im;
156 				break;
157 			case T_DISCON_IND:
158 				/* Peer disconnected. */
159 				NBDEBUG("T_DISCON_IND: reason=%d",
160 				    (int)pptr->discon_ind.DISCON_reason);
161 				goto discon;
162 			case T_ORDREL_IND:
163 				/* Peer disconnecting. */
164 				NBDEBUG("T_ORDREL_IND");
165 				goto discon;
166 			case T_OK_ACK:
167 				switch (pptr->ok_ack.CORRECT_prim) {
168 				case T_DISCON_REQ:
169 					NBDEBUG("T_OK_ACK/T_DISCON_REQ");
170 					goto discon;
171 				default:
172 					NBDEBUG("T_OK_ACK/prim=%d",
173 					    (int)pptr->ok_ack.CORRECT_prim);
174 					goto discon;
175 				}
176 			default:
177 				NBDEBUG("M_PROTO/type=%d", (int)pptr->type);
178 				goto discon;
179 			}
180 			break; /* M_PROTO, M_PCPROTO */
181 
182 		default:
183 			NBDEBUG("unexpected msg type=%d",
184 			    tm->b_datap->db_type);
185 			/*FALLTHROUGH*/
186 discon:
187 			/*
188 			 * The connection is no longer usable.
189 			 * Drop this message and disconnect.
190 			 *
191 			 * Note: nb_disconnect only does t_snddis
192 			 * on the first call, but does important
193 			 * cleanup and state change on any call.
194 			 */
195 			freemsg(tm);
196 			(void) nb_disconnect(nbp);
197 			return (ENOTCONN);
198 		}
199 
200 		/*
201 		 * If we have a data message, append it to
202 		 * the previous chunk(s) and update dlen
203 		 */
204 		if (!tm)
205 			continue;
206 		if (*mpp == NULL) {
207 			*mpp = tm;
208 		} else {
209 			/* Append */
210 			for (im = *mpp; im->b_cont; im = im->b_cont)
211 				;
212 			im->b_cont = tm;
213 		}
214 		dlen += msgdsize(tm);
215 	}
216 
217 	return (error);
218 }
219 
220 /*
221  * Send a T_DISCON_REQ (disconnect)
222  */
223 static int
224 nb_snddis(struct nbpcb *nbp)
225 {
226 	TIUSER *tiptr = nbp->nbp_tiptr;
227 	cred_t *cr = nbp->nbp_cred;
228 	mblk_t *mp;
229 	struct T_discon_req *dreq;
230 	int error, mlen;
231 
232 	ASSERT(MUTEX_HELD(&nbp->nbp_lock));
233 
234 	if (tiptr == NULL)
235 		return (EBADF);
236 
237 	mlen = sizeof (struct T_discon_req);
238 	if (!(mp = allocb_cred_wait(mlen, STR_NOSIG, &error, cr, NOPID)))
239 		return (error);
240 
241 	mp->b_datap->db_type = M_PROTO;
242 	/*LINTED*/
243 	dreq = (struct T_discon_req *)mp->b_wptr;
244 	dreq->PRIM_type = T_DISCON_REQ;
245 	dreq->SEQ_number = -1;
246 	mp->b_wptr += sizeof (struct T_discon_req);
247 
248 	error = tli_send(tiptr, mp, tiptr->fp->f_flag);
249 	/*
250 	 * There is an OK/ACK response expected, which is
251 	 * either handled by our receiver thread, or just
252 	 * discarded if we're closing this endpoint.
253 	 */
254 
255 	return (error);
256 }
257 
258 /*
259  * Stuff the NetBIOS header into space already prepended.
260  */
261 static void
262 nb_sethdr(mblk_t *m, uint8_t type, uint32_t len)
263 {
264 	uint32_t *p;
265 
266 	len &= 0x1FFFF;
267 	len |= (type << 24);
268 
269 	/*LINTED*/
270 	p = (uint32_t *)m->b_rptr;
271 	*p = htonl(len);
272 }
273 
274 /*
275  * Wait for up to 15 sec. for the next packet.
276  * Often return ETIME and do nothing else.
277  * When a packet header is available, check
278  * the header and get the length, but don't
279  * consume it.  No side effects here except
280  * for the pullupmsg call.
281  */
282 static int
283 nbssn_peekhdr(struct nbpcb *nbp, size_t *lenp,	uint8_t *rpcodep)
284 {
285 	uint32_t len, *hdr;
286 	int error;
287 
288 	/*
289 	 * Get the first message (fragment) if
290 	 * we don't already have a left-over.
291 	 */
292 	error = nb_getmsg_mlen(nbp, &nbp->nbp_frag, sizeof (len));
293 	if (error)
294 		return (error);
295 
296 	if (!pullupmsg(nbp->nbp_frag, sizeof (len)))
297 		return (ENOSR);
298 
299 	/*
300 	 * Check the NetBIOS header.
301 	 * (NOT consumed here)
302 	 */
303 	/*LINTED*/
304 	hdr = (uint32_t *)nbp->nbp_frag->b_rptr;
305 
306 	len = ntohl(*hdr);
307 	if ((len >> 16) & 0xFE) {
308 		NBDEBUG("bad nb header received 0x%x (MBZ flag set)\n", len);
309 		return (EPIPE);
310 	}
311 	*rpcodep = (len >> 24) & 0xFF;
312 	switch (*rpcodep) {
313 	case NB_SSN_MESSAGE:
314 	case NB_SSN_REQUEST:
315 	case NB_SSN_POSRESP:
316 	case NB_SSN_NEGRESP:
317 	case NB_SSN_RTGRESP:
318 	case NB_SSN_KEEPALIVE:
319 		break;
320 	default:
321 		NBDEBUG("bad nb header received 0x%x (bogus type)\n", len);
322 		return (EPIPE);
323 	}
324 	len &= 0x1ffff;
325 	if (len > NB_MAXPKTLEN) {
326 		NBDEBUG("packet too long (%d)\n", len);
327 		return (EFBIG);
328 	}
329 	*lenp = len;
330 	return (0);
331 }
332 
333 /*
334  * Receive a NetBIOS message.  This may block to wait for the entire
335  * message to arrive.  The caller knows there is (or should be) a
336  * message to be read.  When we receive and drop a keepalive or
337  * zero-length message, return EAGAIN so the caller knows that
338  * something was received.  This avoids false triggering of the
339  * "server not responding" state machine.
340  *
341  * Calls to this are serialized at a higher level.
342  */
343 static int
344 nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp,
345     uint8_t *rpcodep)
346 {
347 	mblk_t *m0;
348 	uint8_t rpcode;
349 	int error;
350 	size_t rlen, len;
351 
352 	/* We should be the only reader. */
353 	ASSERT(nbp->nbp_flags & NBF_RECVLOCK);
354 
355 	if (nbp->nbp_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  * This is called only by the thread creating this endpoint,
480  * so we're single-threaded here.
481  */
482 static int
483 smb_nbst_create(struct smb_vc *vcp, cred_t *cr)
484 {
485 	TIUSER *tiptr = NULL;
486 	struct nbpcb *nbp = NULL;
487 	dev_t dev;
488 	int rc;
489 	ushort_t fmode;
490 
491 	switch (vcp->vc_srvaddr.sa.sa_family) {
492 	case AF_INET:
493 		dev = nsmb_dev_tcp;
494 		break;
495 	case AF_INET6:
496 		dev = nsmb_dev_tcp6;
497 		break;
498 	default:
499 		return (EAFNOSUPPORT);
500 	}
501 
502 	fmode = FREAD|FWRITE;
503 	rc = t_kopen(NULL, dev, fmode, &tiptr, cr);
504 	if (rc != 0) {
505 		cmn_err(CE_NOTE, "t_kopen failed, rc=%d", rc);
506 		return (rc);
507 	}
508 	ASSERT(tiptr != NULL);
509 
510 	nbp = kmem_zalloc(sizeof (struct nbpcb), KM_SLEEP);
511 
512 	nbp->nbp_timo.tv_sec = SMB_NBTIMO;
513 	nbp->nbp_state = NBST_IDLE;
514 	nbp->nbp_vc = vcp;
515 	nbp->nbp_tiptr = tiptr;
516 	nbp->nbp_fmode = fmode;
517 	nbp->nbp_cred = cr;
518 	crhold(cr);
519 	mutex_init(&nbp->nbp_lock, NULL, MUTEX_DRIVER, NULL);
520 
521 	vcp->vc_tdata = nbp;
522 
523 	return (0);
524 }
525 
526 /*
527  * destroy a transport endpoint
528  *
529  * This is called only by the thread with the last reference
530  * to this endpoint, so we're single-threaded here.
531  */
532 static int
533 smb_nbst_done(struct smb_vc *vcp)
534 {
535 	struct nbpcb *nbp = vcp->vc_tdata;
536 
537 	if (nbp == NULL)
538 		return (ENOTCONN);
539 	vcp->vc_tdata = NULL;
540 
541 	/*
542 	 * Don't really need to disconnect here,
543 	 * because the close following will do it.
544 	 * But it's harmless.
545 	 */
546 	if (nbp->nbp_flags & NBF_CONNECTED)
547 		(void) nb_disconnect(nbp);
548 	if (nbp->nbp_tiptr)
549 		(void) t_kclose(nbp->nbp_tiptr, 0);
550 	if (nbp->nbp_laddr)
551 		smb_free_sockaddr((struct sockaddr *)nbp->nbp_laddr);
552 	if (nbp->nbp_paddr)
553 		smb_free_sockaddr((struct sockaddr *)nbp->nbp_paddr);
554 	if (nbp->nbp_cred)
555 		crfree(nbp->nbp_cred);
556 	mutex_destroy(&nbp->nbp_lock);
557 	kmem_free(nbp, sizeof (*nbp));
558 	return (0);
559 }
560 
561 static int
562 smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap)
563 {
564 	struct nbpcb *nbp = vcp->vc_tdata;
565 	TIUSER *tiptr = nbp->nbp_tiptr;
566 	int err;
567 
568 	/* Only default bind supported. */
569 	if (sap != NULL)
570 		return (ENOTSUP);
571 
572 	err = t_kbind(tiptr, NULL, NULL);
573 
574 	return (err);
575 }
576 
577 static int
578 smb_nbst_unbind(struct smb_vc *vcp)
579 {
580 	struct nbpcb *nbp = vcp->vc_tdata;
581 	TIUSER *tiptr = nbp->nbp_tiptr;
582 	int err;
583 
584 	err = t_kunbind(tiptr);
585 
586 	return (err);
587 }
588 
589 static int
590 smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap)
591 {
592 	struct t_call	call;
593 	struct nbpcb	*nbp = vcp->vc_tdata;
594 	TIUSER		*tiptr = nbp->nbp_tiptr;
595 	int alen, err;
596 
597 	/* Need the address length */
598 	switch (sap->sa_family) {
599 	case AF_INET:
600 		alen = sizeof (struct sockaddr_in);
601 		break;
602 	case AF_INET6:
603 		alen = sizeof (struct sockaddr_in6);
604 		break;
605 	default:
606 		return (EAFNOSUPPORT);
607 	}
608 
609 	/* sockaddr goes in the "addr" netbuf */
610 	bzero(&call, sizeof (call));
611 	call.addr.buf = (char *)sap;
612 	call.addr.len = alen;
613 	call.addr.maxlen = alen;
614 
615 	err = t_kconnect(tiptr, &call, NULL);
616 	if (err != 0)
617 		return (err);
618 
619 	mutex_enter(&nbp->nbp_lock);
620 
621 	nbp->nbp_flags |= NBF_CONNECTED;
622 	nbp->nbp_state = NBST_SESSION;
623 
624 	mutex_exit(&nbp->nbp_lock);
625 
626 	return (0);
627 }
628 
629 static int
630 smb_nbst_disconnect(struct smb_vc *vcp)
631 {
632 	struct nbpcb *nbp = vcp->vc_tdata;
633 
634 	if (nbp == NULL)
635 		return (ENOTCONN);
636 
637 	return (nb_disconnect(nbp));
638 }
639 
640 static int
641 nb_disconnect(struct nbpcb *nbp)
642 {
643 	int err = 0;
644 
645 	mutex_enter(&nbp->nbp_lock);
646 
647 	if ((nbp->nbp_flags & NBF_CONNECTED) != 0) {
648 		nbp->nbp_flags &= ~NBF_CONNECTED;
649 		err = nb_snddis(nbp);
650 	}
651 
652 	mutex_exit(&nbp->nbp_lock);
653 	return (err);
654 }
655 
656 /*
657  * Add the NetBIOS session header and send.
658  *
659  * Calls to this are serialized at a higher level.
660  */
661 static int
662 nbssn_send(struct nbpcb *nbp, mblk_t *m)
663 {
664 	ptrdiff_t diff;
665 	uint32_t mlen;
666 	int error;
667 
668 	/* We should be the only sender. */
669 	ASSERT(nbp->nbp_flags & NBF_SENDLOCK);
670 
671 	if (nbp->nbp_tiptr == NULL) {
672 		error = EBADF;
673 		goto errout;
674 	}
675 
676 	/*
677 	 * Get the message length, which
678 	 * does NOT include the NetBIOS header
679 	 */
680 	mlen = msgdsize(m);
681 
682 	/*
683 	 * Normally, mb_init() will have left space
684 	 * for us to prepend the NetBIOS header in
685 	 * the data block of the first mblk.
686 	 * However, we have to check in case other
687 	 * code did not leave this space, or if the
688 	 * message is from dupmsg (db_ref > 1)
689 	 *
690 	 * If don't find room in the first data block,
691 	 * we have to allocb a new message and link it
692 	 * on the front of the chain.  We try not to
693 	 * do this becuase it's less efficient.  Also,
694 	 * some network drivers will apparently send
695 	 * each mblk in the chain as separate frames.
696 	 * (That's arguably a driver bug.)
697 	 *
698 	 * Not bothering with allocb_cred_wait below
699 	 * because the message we're prepending to
700 	 * should already have a db_credp.
701 	 */
702 
703 	diff = MBLKHEAD(m);
704 	if (diff == 4 && DB_REF(m) == 1) {
705 		/* We can use the first dblk. */
706 		m->b_rptr -= 4;
707 	} else {
708 		/* Link a new mblk on the head. */
709 		mblk_t *m0;
710 
711 		/* M_PREPEND */
712 		m0 = allocb_wait(4, BPRI_LO, STR_NOSIG, &error);
713 		if (m0 == NULL)
714 			goto errout;
715 
716 		m0->b_wptr += 4;
717 		m0->b_cont = m;
718 		m = m0;
719 	}
720 
721 	nb_sethdr(m, NB_SSN_MESSAGE, mlen);
722 	error = tli_send(nbp->nbp_tiptr, m, 0);
723 	return (error);
724 
725 errout:
726 	if (m != NULL)
727 		m_freem(m);
728 	return (error);
729 }
730 
731 /*
732  * Always consume the message.
733  * (On error too!)
734  */
735 static int
736 smb_nbst_send(struct smb_vc *vcp, mblk_t *m)
737 {
738 	struct nbpcb *nbp = vcp->vc_tdata;
739 	int err;
740 
741 	mutex_enter(&nbp->nbp_lock);
742 	if ((nbp->nbp_flags & NBF_CONNECTED) == 0) {
743 		err = ENOTCONN;
744 		goto out;
745 	}
746 	if (nbp->nbp_flags & NBF_SENDLOCK) {
747 		NBDEBUG("multiple smb_nbst_send!\n");
748 		err = EWOULDBLOCK;
749 		goto out;
750 	}
751 	nbp->nbp_flags |= NBF_SENDLOCK;
752 	mutex_exit(&nbp->nbp_lock);
753 
754 	err = nbssn_send(nbp, m);
755 	m = NULL; /* nbssn_send always consumes this */
756 
757 	mutex_enter(&nbp->nbp_lock);
758 	nbp->nbp_flags &= ~NBF_SENDLOCK;
759 	if (nbp->nbp_flags & NBF_LOCKWAIT) {
760 		nbp->nbp_flags &= ~NBF_LOCKWAIT;
761 		cv_broadcast(&nbp->nbp_cv);
762 	}
763 out:
764 	mutex_exit(&nbp->nbp_lock);
765 	if (m != NULL)
766 		m_freem(m);
767 	return (err);
768 }
769 
770 static int
771 smb_nbst_recv(struct smb_vc *vcp, mblk_t **mpp)
772 {
773 	struct nbpcb *nbp = vcp->vc_tdata;
774 	uint8_t rpcode;
775 	int err, rplen;
776 
777 	mutex_enter(&nbp->nbp_lock);
778 	if ((nbp->nbp_flags & NBF_CONNECTED) == 0) {
779 		err = ENOTCONN;
780 		goto out;
781 	}
782 	if (nbp->nbp_flags & NBF_RECVLOCK) {
783 		NBDEBUG("multiple smb_nbst_recv!\n");
784 		err = EWOULDBLOCK;
785 		goto out;
786 	}
787 	nbp->nbp_flags |= NBF_RECVLOCK;
788 	mutex_exit(&nbp->nbp_lock);
789 
790 	err = nbssn_recv(nbp, mpp, &rplen, &rpcode);
791 
792 	mutex_enter(&nbp->nbp_lock);
793 	nbp->nbp_flags &= ~NBF_RECVLOCK;
794 	if (nbp->nbp_flags & NBF_LOCKWAIT) {
795 		nbp->nbp_flags &= ~NBF_LOCKWAIT;
796 		cv_broadcast(&nbp->nbp_cv);
797 	}
798 out:
799 	mutex_exit(&nbp->nbp_lock);
800 	return (err);
801 }
802 
803 /*
804  * Wait for up to "ticks" clock ticks for input on vcp.
805  * Returns zero if input is available, otherwise ETIME
806  * indicating time expired, or other error codes.
807  */
808 /*ARGSUSED*/
809 static int
810 smb_nbst_poll(struct smb_vc *vcp, int ticks)
811 {
812 	return (ENOTSUP);
813 }
814 
815 /*ARGSUSED*/
816 static int
817 smb_nbst_getparam(struct smb_vc *vcp, int param, void *data)
818 {
819 	return (EINVAL);
820 }
821 
822 static int
823 smb_nbst_setparam(struct smb_vc *vcp, int param, void *data)
824 {
825 	struct t_optmgmt oreq, ores;
826 	struct {
827 		struct T_opthdr oh;
828 		int ival;
829 	} opts;
830 	struct nbpcb *nbp = vcp->vc_tdata;
831 	int level, name, err;
832 
833 	switch (param) {
834 	case SMBTP_TCP_NODELAY:
835 		level = IPPROTO_TCP;
836 		name = TCP_NODELAY;
837 		break;
838 
839 	case SMBTP_TCP_CON_TMO:	/* int mSec */
840 		level = IPPROTO_TCP;
841 		name = TCP_CONN_ABORT_THRESHOLD;
842 		break;
843 
844 	case SMBTP_KEEPALIVE:	// SO_KEEPALIVE
845 	case SMBTP_SNDBUF:	// SO_SNDBUF
846 	case SMBTP_RCVBUF:	// SO_RCVBUF
847 	case SMBTP_RCVTIMEO:	// SO_RCVTIMEO
848 		level = SOL_SOCKET;
849 		name = param;
850 		break;
851 
852 	default:
853 		return (EINVAL);
854 	}
855 
856 	/* opt header */
857 	opts.oh.len = sizeof (opts);
858 	opts.oh.level = level;
859 	opts.oh.name = name;
860 	opts.oh.status = 0;
861 	opts.ival = *(int *)data;
862 
863 	oreq.flags = T_NEGOTIATE;
864 	oreq.opt.buf = (void *)&opts;
865 	oreq.opt.len = sizeof (opts);
866 	oreq.opt.maxlen = oreq.opt.len;
867 
868 	ores.flags = 0;
869 	ores.opt.buf = NULL;
870 	ores.opt.len = 0;
871 	ores.opt.maxlen = 0;
872 
873 	err = t_koptmgmt(nbp->nbp_tiptr, &oreq, &ores);
874 	if (err != 0) {
875 		cmn_err(CE_NOTE, "t_opgmgnt, err = %d", err);
876 		return (EPROTO);
877 	}
878 
879 	if ((ores.flags & T_SUCCESS) == 0) {
880 		cmn_err(CE_NOTE, "smb_nbst_setparam: "
881 		    "flags 0x%x, status 0x%x",
882 		    (int)ores.flags, (int)opts.oh.status);
883 		return (EPROTO);
884 	}
885 
886 	return (0);
887 }
888 
889 /*
890  * Check for fatal errors
891  */
892 /*ARGSUSED*/
893 static int
894 smb_nbst_fatal(struct smb_vc *vcp, int error)
895 {
896 	switch (error) {
897 	case ENOTCONN:
898 	case ENETRESET:
899 	case ECONNABORTED:
900 	case EPIPE:
901 		return (1);
902 	}
903 	return (0);
904 }
905 
906 
907 struct smb_tran_desc smb_tran_nbtcp_desc = {
908 	SMBT_NBTCP,
909 	smb_nbst_create,
910 	smb_nbst_done,
911 	smb_nbst_bind,
912 	smb_nbst_unbind,
913 	smb_nbst_connect,
914 	smb_nbst_disconnect,
915 	smb_nbst_send,
916 	smb_nbst_recv,
917 	smb_nbst_poll,
918 	smb_nbst_getparam,
919 	smb_nbst_setparam,
920 	smb_nbst_fatal,
921 	{NULL, NULL}
922 };
923