Print this page
NEX-16824 SMB client connection setup rework
NEX-17232 SMB client reconnect failures
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (improve debug)
NEX-16818 Add fksmbcl development tool
NEX-17264 SMB client test tp_smbutil_013 fails after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (fix ref leaks)
SUP-538 System panic in NULL pointer dereference in nsmb'nbssn_peekhdr().
        
*** 32,42 ****
   * $Id: smb_trantcp.c,v 1.39 2005/03/02 01:27:44 lindak Exp $
   */
  /*
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
!  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
   */
  
  #include <sys/param.h>
  #include <sys/systm.h>
  #include <sys/autoconf.h>
--- 32,43 ----
   * $Id: smb_trantcp.c,v 1.39 2005/03/02 01:27:44 lindak Exp $
   */
  /*
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
!  *
!  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
   */
  
  #include <sys/param.h>
  #include <sys/systm.h>
  #include <sys/autoconf.h>
*** 71,87 ****
  #include <netsmb/smb_conn.h>
  #include <netsmb/smb_subr.h>
  #include <netsmb/smb_tran.h>
  #include <netsmb/smb_trantcp.h>
  
- /*
-  * SMB messages are up to 64K.
-  * Let's leave room for two.
-  */
- static int smb_tcpsndbuf = 0x20000;
- static int smb_tcprcvbuf = 0x20000;
- 
  static int  nb_disconnect(struct nbpcb *nbp);
  
  
  /*
   * Get mblks into *mpp until the data length is at least mlen.
--- 72,81 ----
*** 161,171 ****
                                  tm = im;
                                  break;
                          case T_DISCON_IND:
                                  /* Peer disconnected. */
                                  NBDEBUG("T_DISCON_IND: reason=%d",
!                                     pptr->discon_ind.DISCON_reason);
                                  goto discon;
                          case T_ORDREL_IND:
                                  /* Peer disconnecting. */
                                  NBDEBUG("T_ORDREL_IND");
                                  goto discon;
--- 155,165 ----
                                  tm = im;
                                  break;
                          case T_DISCON_IND:
                                  /* Peer disconnected. */
                                  NBDEBUG("T_DISCON_IND: reason=%d",
!                                     (int)pptr->discon_ind.DISCON_reason);
                                  goto discon;
                          case T_ORDREL_IND:
                                  /* Peer disconnecting. */
                                  NBDEBUG("T_ORDREL_IND");
                                  goto discon;
*** 174,188 ****
                                  case T_DISCON_REQ:
                                          NBDEBUG("T_OK_ACK/T_DISCON_REQ");
                                          goto discon;
                                  default:
                                          NBDEBUG("T_OK_ACK/prim=%d",
!                                             pptr->ok_ack.CORRECT_prim);
                                          goto discon;
                                  }
                          default:
!                                 NBDEBUG("M_PROTO/type=%d", pptr->type);
                                  goto discon;
                          }
                          break; /* M_PROTO, M_PCPROTO */
  
                  default:
--- 168,182 ----
                                  case T_DISCON_REQ:
                                          NBDEBUG("T_OK_ACK/T_DISCON_REQ");
                                          goto discon;
                                  default:
                                          NBDEBUG("T_OK_ACK/prim=%d",
!                                             (int)pptr->ok_ack.CORRECT_prim);
                                          goto discon;
                                  }
                          default:
!                                 NBDEBUG("M_PROTO/type=%d", (int)pptr->type);
                                  goto discon;
                          }
                          break; /* M_PROTO, M_PCPROTO */
  
                  default:
*** 483,508 ****
   * SMB transport interface
   *
   * This is called only by the thread creating this endpoint,
   * so we're single-threaded here.
   */
- /*ARGSUSED*/
  static int
  smb_nbst_create(struct smb_vc *vcp, cred_t *cr)
  {
!         struct nbpcb *nbp;
  
          nbp = kmem_zalloc(sizeof (struct nbpcb), KM_SLEEP);
  
          nbp->nbp_timo.tv_sec = SMB_NBTIMO;
!         nbp->nbp_state = NBST_CLOSED; /* really IDLE */
          nbp->nbp_vc = vcp;
!         nbp->nbp_sndbuf = smb_tcpsndbuf;
!         nbp->nbp_rcvbuf = smb_tcprcvbuf;
          nbp->nbp_cred = cr;
          crhold(cr);
          mutex_init(&nbp->nbp_lock, NULL, MUTEX_DRIVER, NULL);
          vcp->vc_tdata = nbp;
  
          return (0);
  }
  
--- 477,525 ----
   * SMB transport interface
   *
   * This is called only by the thread creating this endpoint,
   * so we're single-threaded here.
   */
  static int
  smb_nbst_create(struct smb_vc *vcp, cred_t *cr)
  {
!         TIUSER *tiptr = NULL;
!         struct nbpcb *nbp = NULL;
!         dev_t dev;
!         int rc;
!         ushort_t fmode;
  
+         switch (vcp->vc_srvaddr.sa.sa_family) {
+         case AF_INET:
+                 dev = nsmb_dev_tcp;
+                 break;
+         case AF_INET6:
+                 dev = nsmb_dev_tcp6;
+                 break;
+         default:
+                 return (EAFNOSUPPORT);
+         }
+ 
+         fmode = FREAD|FWRITE;
+         rc = t_kopen(NULL, dev, fmode, &tiptr, cr);
+         if (rc != 0) {
+                 cmn_err(CE_NOTE, "t_kopen failed, rc=%d", rc);
+                 return (rc);
+         }
+         ASSERT(tiptr != NULL);
+ 
          nbp = kmem_zalloc(sizeof (struct nbpcb), KM_SLEEP);
  
          nbp->nbp_timo.tv_sec = SMB_NBTIMO;
!         nbp->nbp_state = NBST_IDLE;
          nbp->nbp_vc = vcp;
!         nbp->nbp_tiptr = tiptr;
!         nbp->nbp_fmode = fmode;
          nbp->nbp_cred = cr;
          crhold(cr);
          mutex_init(&nbp->nbp_lock, NULL, MUTEX_DRIVER, NULL);
+ 
          vcp->vc_tdata = nbp;
  
          return (0);
  }
  
*** 539,653 ****
          mutex_destroy(&nbp->nbp_lock);
          kmem_free(nbp, sizeof (*nbp));
          return (0);
  }
  
- /*
-  * Loan a transport file pointer (from user space) to this
-  * IOD endpoint.  There should be no other thread using this
-  * endpoint when we do this, but lock for consistency.
-  */
  static int
! nb_loan_fp(struct nbpcb *nbp, struct file *fp, cred_t *cr)
  {
!         TIUSER *tiptr;
          int err;
  
!         err = t_kopen(fp, 0, 0, &tiptr, cr);
!         if (err != 0)
!                 return (err);
  
!         mutex_enter(&nbp->nbp_lock);
  
!         nbp->nbp_tiptr = tiptr;
!         nbp->nbp_fmode = tiptr->fp->f_flag;
!         nbp->nbp_flags |= NBF_CONNECTED;
!         nbp->nbp_state = NBST_SESSION;
! 
!         mutex_exit(&nbp->nbp_lock);
! 
!         return (0);
  }
  
! /*
!  * Take back the transport file pointer we previously loaned.
!  * It's possible there may be another thread in here, so let
!  * others get out of the way before we pull the rug out.
!  *
!  * Some notes about the locking here:  The higher-level IOD code
!  * serializes activity such that at most one reader and writer
!  * thread can be active in this code (and possibly both).
!  * Keeping nbp_lock held during the activities of these two
!  * threads would lead to the possibility of nbp_lock being
!  * held by a blocked thread, so this instead sets one of the
!  * flags (NBF_SENDLOCK | NBF_RECVLOCK) when a sender or a
!  * receiver is active (respectively).  Lastly, tear-down is
!  * the only tricky bit (here) where we must wait for any of
!  * these activities to get out of current calls so they will
!  * notice that we've turned off the NBF_CONNECTED flag.
!  */
! static void
! nb_unloan_fp(struct nbpcb *nbp)
  {
  
!         mutex_enter(&nbp->nbp_lock);
  
!         nbp->nbp_flags &= ~NBF_CONNECTED;
!         while (nbp->nbp_flags & (NBF_SENDLOCK | NBF_RECVLOCK)) {
!                 nbp->nbp_flags |= NBF_LOCKWAIT;
!                 cv_wait(&nbp->nbp_cv, &nbp->nbp_lock);
!         }
!         if (nbp->nbp_frag != NULL) {
!                 freemsg(nbp->nbp_frag);
!                 nbp->nbp_frag = NULL;
!         }
!         if (nbp->nbp_tiptr != NULL) {
!                 (void) t_kclose(nbp->nbp_tiptr, 0);
!                 nbp->nbp_tiptr = NULL;
!         }
!         nbp->nbp_state = NBST_CLOSED;
! 
!         mutex_exit(&nbp->nbp_lock);
  }
  
  static int
! smb_nbst_loan_fp(struct smb_vc *vcp, struct file *fp, cred_t *cr)
  {
          struct nbpcb *nbp = vcp->vc_tdata;
!         int error = 0;
  
!         /*
!          * Un-loan the existing one, if any.
!          */
!         (void) nb_disconnect(nbp);
!         nb_unloan_fp(nbp);
! 
!         /*
!          * Loan the new one passed in.
!          */
!         if (fp != NULL) {
!                 error = nb_loan_fp(nbp, fp, cr);
          }
  
!         return (error);
! }
  
! /*ARGSUSED*/
! static int
! smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap)
! {
!         return (ENOTSUP);
! }
  
! /*ARGSUSED*/
! static int
! smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap)
! {
!         return (ENOTSUP);
  }
  
- /*ARGSUSED*/
  static int
  smb_nbst_disconnect(struct smb_vc *vcp)
  {
          struct nbpcb *nbp = vcp->vc_tdata;
  
--- 556,633 ----
          mutex_destroy(&nbp->nbp_lock);
          kmem_free(nbp, sizeof (*nbp));
          return (0);
  }
  
  static int
! smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap)
  {
!         struct nbpcb *nbp = vcp->vc_tdata;
!         TIUSER *tiptr = nbp->nbp_tiptr;
          int err;
  
!         /* Only default bind supported. */
!         if (sap != NULL)
!                 return (ENOTSUP);
  
!         err = t_kbind(tiptr, NULL, NULL);
  
!         return (err);
  }
  
! static int
! smb_nbst_unbind(struct smb_vc *vcp)
  {
+         struct nbpcb *nbp = vcp->vc_tdata;
+         TIUSER *tiptr = nbp->nbp_tiptr;
+         int err;
  
!         err = t_kunbind(tiptr);
  
!         return (err);
  }
  
  static int
! smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap)
  {
+         struct t_call   call;
          struct nbpcb    *nbp = vcp->vc_tdata;
!         TIUSER          *tiptr = nbp->nbp_tiptr;
!         int alen, err;
  
!         /* Need the address length */
!         switch (sap->sa_family) {
!         case AF_INET:
!                 alen = sizeof (struct sockaddr_in);
!                 break;
!         case AF_INET6:
!                 alen = sizeof (struct sockaddr_in6);
!                 break;
!         default:
!                 return (EAFNOSUPPORT);
          }
  
!         /* sockaddr goes in the "addr" netbuf */
!         bzero(&call, sizeof (call));
!         call.addr.buf = (char *)sap;
!         call.addr.len = alen;
!         call.addr.maxlen = alen;
  
!         err = t_kconnect(tiptr, &call, NULL);
!         if (err != 0)
!                 return (err);
  
!         mutex_enter(&nbp->nbp_lock);
! 
!         nbp->nbp_flags |= NBF_CONNECTED;
!         nbp->nbp_state = NBST_SESSION;
! 
!         mutex_exit(&nbp->nbp_lock);
! 
!         return (0);
  }
  
  static int
  smb_nbst_disconnect(struct smb_vc *vcp)
  {
          struct nbpcb *nbp = vcp->vc_tdata;
  
*** 830,877 ****
  smb_nbst_poll(struct smb_vc *vcp, int ticks)
  {
          return (ENOTSUP);
  }
  
  static int
  smb_nbst_getparam(struct smb_vc *vcp, int param, void *data)
  {
          struct nbpcb *nbp = vcp->vc_tdata;
  
          switch (param) {
!         case SMBTP_SNDSZ:
!                 *(int *)data = nbp->nbp_sndbuf;
                  break;
!         case SMBTP_RCVSZ:
!                 *(int *)data = nbp->nbp_rcvbuf;
                  break;
!         case SMBTP_TIMEOUT:
!                 *(struct timespec *)data = nbp->nbp_timo;
                  break;
! #ifdef SMBTP_SELECTID
!         case SMBTP_SELECTID:
!                 *(void **)data = nbp->nbp_selectid;
!                 break;
! #endif
! #ifdef SMBTP_UPCALL
!         case SMBTP_UPCALL:
!                 *(void **)data = nbp->nbp_upcall;
!                 break;
! #endif
          default:
                  return (EINVAL);
          }
          return (0);
  }
  
- /*ARGSUSED*/
- static int
- smb_nbst_setparam(struct smb_vc *vcp, int param, void *data)
- {
-         return (EINVAL);
- }
- 
  /*
   * Check for fatal errors
   */
  /*ARGSUSED*/
  static int
--- 810,893 ----
  smb_nbst_poll(struct smb_vc *vcp, int ticks)
  {
          return (ENOTSUP);
  }
  
+ /*ARGSUSED*/
  static int
  smb_nbst_getparam(struct smb_vc *vcp, int param, void *data)
  {
+         return (EINVAL);
+ }
+ 
+ static int
+ smb_nbst_setparam(struct smb_vc *vcp, int param, void *data)
+ {
+         struct t_optmgmt oreq, ores;
+         struct {
+                 struct T_opthdr oh;
+                 int ival;
+         } opts;
          struct nbpcb *nbp = vcp->vc_tdata;
+         int level, name, err;
  
          switch (param) {
!         case SMBTP_TCP_NODELAY:
!                 level = IPPROTO_TCP;
!                 name = TCP_NODELAY;
                  break;
! 
!         case SMBTP_TCP_CON_TMO: /* int mSec */
!                 level = IPPROTO_TCP;
!                 name = TCP_CONN_ABORT_THRESHOLD;
                  break;
! 
!         case SMBTP_KEEPALIVE:   // SO_KEEPALIVE
!         case SMBTP_SNDBUF:      // SO_SNDBUF
!         case SMBTP_RCVBUF:      // SO_RCVBUF
!         case SMBTP_RCVTIMEO:    // SO_RCVTIMEO
!                 level = SOL_SOCKET;
!                 name = param;
                  break;
! 
          default:
                  return (EINVAL);
          }
+ 
+         /* opt header */
+         opts.oh.len = sizeof (opts);
+         opts.oh.level = level;
+         opts.oh.name = name;
+         opts.oh.status = 0;
+         opts.ival = *(int *)data;
+ 
+         oreq.flags = T_NEGOTIATE;
+         oreq.opt.buf = (void *)&opts;
+         oreq.opt.len = sizeof (opts);
+         oreq.opt.maxlen = oreq.opt.len;
+ 
+         ores.flags = 0;
+         ores.opt.buf = NULL;
+         ores.opt.len = 0;
+         ores.opt.maxlen = 0;
+ 
+         err = t_koptmgmt(nbp->nbp_tiptr, &oreq, &ores);
+         if (err != 0) {
+                 cmn_err(CE_NOTE, "t_opgmgnt, err = %d", err);
+                 return (EPROTO);
+         }
+ 
+         if ((ores.flags & T_SUCCESS) == 0) {
+                 cmn_err(CE_NOTE, "smb_nbst_setparam: "
+                     "flags 0x%x, status 0x%x",
+                     (int)ores.flags, (int)opts.oh.status);
+                 return (EPROTO);
+         }
+ 
          return (0);
  }
  
  /*
   * Check for fatal errors
   */
  /*ARGSUSED*/
  static int
*** 891,906 ****
  struct smb_tran_desc smb_tran_nbtcp_desc = {
          SMBT_NBTCP,
          smb_nbst_create,
          smb_nbst_done,
          smb_nbst_bind,
          smb_nbst_connect,
          smb_nbst_disconnect,
          smb_nbst_send,
          smb_nbst_recv,
          smb_nbst_poll,
-         smb_nbst_loan_fp,
          smb_nbst_getparam,
          smb_nbst_setparam,
          smb_nbst_fatal,
          {NULL, NULL}
  };
--- 907,922 ----
  struct smb_tran_desc smb_tran_nbtcp_desc = {
          SMBT_NBTCP,
          smb_nbst_create,
          smb_nbst_done,
          smb_nbst_bind,
+         smb_nbst_unbind,
          smb_nbst_connect,
          smb_nbst_disconnect,
          smb_nbst_send,
          smb_nbst_recv,
          smb_nbst_poll,
          smb_nbst_getparam,
          smb_nbst_setparam,
          smb_nbst_fatal,
          {NULL, NULL}
  };