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 2008 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  27 /*        All Rights Reserved   */
  28 
  29 /*
  30  * University Copyright- Copyright (c) 1982, 1986, 1988
  31  * The Regents of the University of California
  32  * All Rights Reserved
  33  *
  34  * University Acknowledgment- Portions of this document are derived from
  35  * software developed by the University of California, Berkeley, and its
  36  * contributors.
  37  */
  38 
  39 #pragma ident   "%Z%%M% %I%     %E% SMI"
  40 
  41 /*
  42  * Kernel TLI-like function to read a datagram off of a
  43  * transport endpoints stream head.
  44  *
  45  * Returns:
  46  *      0       On success or positive error code.
  47  *              On sucess, type is set to:
  48  *      T_DATA          If normal data has been received
  49  *      T_UDERR         If an error indication has been received,
  50  *                      in which case uderr contains the unitdata
  51  *                      error number.
  52  *      T_ERROR
  53  */
  54 
  55 #include <sys/param.h>
  56 #include <sys/types.h>
  57 #include <sys/user.h>
  58 #include <sys/file.h>
  59 #include <sys/errno.h>
  60 #include <sys/stream.h>
  61 #include <sys/strsubr.h>
  62 #include <sys/vnode.h>
  63 #include <sys/ioctl.h>
  64 #include <sys/stropts.h>
  65 #include <sys/tihdr.h>
  66 #include <sys/timod.h>
  67 #include <sys/tiuser.h>
  68 #include <sys/t_kuser.h>
  69 #include <sys/sysmacros.h>
  70 #include <sys/strsun.h>
  71 
  72 
  73 int
  74 t_krcvudata(TIUSER *tiptr, struct t_kunitdata *unitdata, int *type, int *uderr)
  75 {
  76         int                     len;
  77         size_t                  hdrsz;
  78         union T_primitives      *pptr;
  79         struct file             *fp;
  80         mblk_t                  *bp;
  81         mblk_t                  *nbp;
  82         mblk_t                  *mp;
  83         mblk_t                  *tmp;
  84         int                     error;
  85         int                     flag;
  86 
  87         fp = tiptr->fp;
  88 
  89         if (type == NULL || uderr == NULL)
  90                 return (EINVAL);
  91 
  92         error = 0;
  93         unitdata->udata.buf = NULL;
  94 
  95         if (unitdata->udata.udata_mp) {
  96                 KTLILOG(2, "t_krcvudata: freeing existing message block\n", 0);
  97                 freemsg(unitdata->udata.udata_mp);
  98                 unitdata->udata.udata_mp = NULL;
  99         }
 100 
 101         /*
 102          * XXX  Grabbing a mutex to do an atomic operation seems pointless
 103          */
 104         mutex_enter(&fp->f_tlock);
 105         flag = fp->f_flag;
 106         mutex_exit(&fp->f_tlock);
 107 
 108         if ((error = tli_recv(tiptr, &bp, flag)) != 0)
 109                 return (error);
 110 
 111         /*
 112          * Got something
 113          */
 114         switch (bp->b_datap->db_type) {
 115         case M_PROTO:
 116                 /* LINTED pointer alignment */
 117                 pptr = (union T_primitives *)bp->b_rptr;
 118                 switch (pptr->type) {
 119                 case T_UNITDATA_IND:
 120                         KTLILOG(2, "t_krcvudata: Got T_UNITDATA_IND\n", 0);
 121                         hdrsz = MBLKL(bp);
 122 
 123                         /*
 124                          * check everything for consistency
 125                          */
 126                         if (hdrsz < TUNITDATAINDSZ ||
 127                             hdrsz < (pptr->unitdata_ind.OPT_length +
 128                             pptr->unitdata_ind.OPT_offset) ||
 129                             hdrsz < (pptr->unitdata_ind.SRC_length +
 130                             pptr->unitdata_ind.SRC_offset)) {
 131                                 error = EPROTO;
 132                                 freemsg(bp);
 133                                 break;
 134                         }
 135 
 136                         /*
 137                          * okay, so now we copy them
 138                          */
 139                         len = MIN(pptr->unitdata_ind.SRC_length,
 140                             unitdata->addr.maxlen);
 141                         bcopy(bp->b_rptr + pptr->unitdata_ind.SRC_offset,
 142                             unitdata->addr.buf, len);
 143                         unitdata->addr.len = len;
 144 
 145                         len = MIN(pptr->unitdata_ind.OPT_length,
 146                             unitdata->opt.maxlen);
 147                         bcopy(bp->b_rptr + pptr->unitdata_ind.OPT_offset,
 148                             unitdata->opt.buf, len);
 149                         unitdata->opt.len = len;
 150 
 151                         bp->b_rptr += hdrsz;
 152 
 153                         /*
 154                          * we assume that the client knows how to deal
 155                          * with a set of linked mblks, so all we do is
 156                          * make a pass and remove any that are zero
 157                          * length.
 158                          */
 159                         nbp = NULL;
 160                         mp = bp;
 161                         while (mp) {
 162                                 if (bp->b_wptr == bp->b_rptr) {
 163                                         KTLILOG(2,
 164                                             "t_krcvudata: zero length block\n",
 165                                             0);
 166                                         tmp = mp->b_cont;
 167                                         if (nbp)
 168                                                 nbp->b_cont = tmp;
 169                                         else
 170                                                 bp = tmp;
 171 
 172                                         freeb(mp);
 173                                         mp = tmp;
 174                                 } else {
 175                                         nbp = mp;
 176                                         mp = mp->b_cont;
 177                                 }
 178                         }
 179 #ifdef KTLIDEBUG
 180 {
 181         mblk_t *tp;
 182 
 183         tp = bp;
 184         while (tp) {
 185                 struct datab *dbp = tp->b_datap;
 186                 frtn_t *frp = dbp->db_frtnp;
 187 
 188                 KTLILOG(2, "t_krcvudata: bp %x, ", tp);
 189                 KTLILOG(2, "db_size %x, ", dbp->db_lim - dbp->db_base);
 190                 KTLILOG(2, "db_ref %x", dbp->db_ref);
 191 
 192                 if (frp != NULL)
 193                         KTLILOG(2, ", func: %x", frp->free_func);
 194                         KTLILOG(2, ", arg %x\n", frp->free_arg);
 195                 } else
 196                         KTLILOG(2, "\n", 0);
 197                 tp = tp->b_cont;
 198         }
 199 }
 200 #endif /* KTLIDEBUG */
 201                         /*
 202                          * now just point the users mblk
 203                          * pointer to what we received.
 204                          */
 205                         if (bp == NULL) {
 206                                 KTLILOG(2, "t_krcvudata: No data\n", 0);
 207                                 error = EPROTO;
 208                                 break;
 209                         }
 210                         if (bp->b_wptr != bp->b_rptr) {
 211                             if (!IS_P2ALIGNED(bp->b_rptr, sizeof (uint32_t)))
 212                                         if (!pullupmsg(bp, MBLKL(bp))) {
 213                                                 KTLILOG(1,
 214                                         "t_krcvudata:  pullupmsg failed\n", 0);
 215                                                 error = EIO;
 216                                                 freemsg(bp);
 217                                                 break;
 218                                         }
 219                                 unitdata->udata.buf = (char *)bp->b_rptr;
 220                                 unitdata->udata.len = (uint_t)MBLKL(bp);
 221 
 222                                 KTLILOG(2, "t_krcvudata: got %d bytes\n",
 223                                     unitdata->udata.len);
 224                                 unitdata->udata.udata_mp = bp;
 225                         } else {
 226                                 KTLILOG(2,
 227                                     "t_krcvudata: 0 length data message\n", 0);
 228                                 freemsg(bp);
 229                                 unitdata->udata.len = 0;
 230                         }
 231                         *type = T_DATA;
 232                         break;
 233 
 234                 case T_UDERROR_IND:
 235                         KTLILOG(2, "t_krcvudata: Got T_UDERROR_IND\n", 0);
 236                         hdrsz = MBLKL(bp);
 237 
 238                         /*
 239                          * check everything for consistency
 240                          */
 241                         if (hdrsz < TUDERRORINDSZ ||
 242                             hdrsz < (pptr->uderror_ind.OPT_length +
 243                             pptr->uderror_ind.OPT_offset) ||
 244                             hdrsz < (pptr->uderror_ind.DEST_length +
 245                             pptr->uderror_ind.DEST_offset)) {
 246                                 error = EPROTO;
 247                                 freemsg(bp);
 248                                 break;
 249                         }
 250 
 251                         if (pptr->uderror_ind.DEST_length >
 252                             (int)unitdata->addr.maxlen ||
 253                             pptr->uderror_ind.OPT_length >
 254                             (int)unitdata->opt.maxlen) {
 255                                 error = EMSGSIZE;
 256                                 freemsg(bp);
 257                                 break;
 258                         }
 259 
 260                         /*
 261                          * okay, so now we copy them
 262                          */
 263                         bcopy(bp->b_rptr + pptr->uderror_ind.DEST_offset,
 264                             unitdata->addr.buf,
 265                             (size_t)pptr->uderror_ind.DEST_length);
 266                         unitdata->addr.len = pptr->uderror_ind.DEST_length;
 267 
 268                         bcopy(bp->b_rptr + pptr->uderror_ind.OPT_offset,
 269                             unitdata->opt.buf,
 270                             (size_t)pptr->uderror_ind.OPT_length);
 271                         unitdata->opt.len = pptr->uderror_ind.OPT_length;
 272 
 273                         *uderr = pptr->uderror_ind.ERROR_type;
 274 
 275                         unitdata->udata.buf = NULL;
 276                         unitdata->udata.udata_mp = NULL;
 277                         unitdata->udata.len = 0;
 278 
 279                         freemsg(bp);
 280 
 281                         *type = T_UDERR;
 282                         break;
 283 
 284                 default:
 285                         KTLILOG(1,
 286                             "t_krcvudata: Unknown transport primitive %d\n",
 287                             pptr->type);
 288                         error = EPROTO;
 289                         freemsg(bp);
 290                         break;
 291                 }
 292                 break;
 293 
 294         case M_FLUSH:
 295                 KTLILOG(1, "t_krcvudata: tli_recv returned M_FLUSH\n", 0);
 296                 freemsg(bp);
 297                 *type = T_ERROR;
 298                 break;
 299 
 300         default:
 301                 KTLILOG(1, "t_krcvudata: unknown message type %x\n",
 302                     bp->b_datap->db_type);
 303                 freemsg(bp);
 304                 *type = T_ERROR;
 305                 break;
 306         }
 307 
 308         return (error);
 309 }