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 
  27 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  28 /*        All Rights Reserved   */
  29 
  30 
  31 /*
  32  * STREAMS Administrative Driver
  33  *
  34  * Currently only handles autopush and module name verification.
  35  */
  36 
  37 #include <sys/types.h>
  38 #include <sys/param.h>
  39 #include <sys/errno.h>
  40 #include <sys/stream.h>
  41 #include <sys/stropts.h>
  42 #include <sys/strsubr.h>
  43 #include <sys/strsun.h>
  44 #include <sys/conf.h>
  45 #include <sys/sad.h>
  46 #include <sys/cred.h>
  47 #include <sys/debug.h>
  48 #include <sys/ddi.h>
  49 #include <sys/sunddi.h>
  50 #include <sys/stat.h>
  51 #include <sys/cmn_err.h>
  52 #include <sys/systm.h>
  53 #include <sys/modctl.h>
  54 #include <sys/sysmacros.h>
  55 #include <sys/zone.h>
  56 #include <sys/policy.h>
  57 
  58 static int sadopen(queue_t *, dev_t *, int, int, cred_t *);
  59 static int sadclose(queue_t *, int, cred_t *);
  60 static int sadwput(queue_t *qp, mblk_t *mp);
  61 
  62 static int sad_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
  63 static int sad_attach(dev_info_t *, ddi_attach_cmd_t);
  64 
  65 static void apush_ioctl(), apush_iocdata();
  66 static void vml_ioctl(), vml_iocdata();
  67 static int valid_major(major_t);
  68 
  69 static dev_info_t *sad_dip;             /* private copy of devinfo pointer */
  70 
  71 static struct module_info sad_minfo = {
  72         0x7361, "sad", 0, INFPSZ, 0, 0
  73 };
  74 
  75 static struct qinit sad_rinit = {
  76         NULL, NULL, sadopen, sadclose, NULL, &sad_minfo, NULL
  77 };
  78 
  79 static struct qinit sad_winit = {
  80         sadwput, NULL, NULL, NULL, NULL, &sad_minfo, NULL
  81 };
  82 
  83 struct streamtab sadinfo = {
  84         &sad_rinit, &sad_winit, NULL, NULL
  85 };
  86 
  87 DDI_DEFINE_STREAM_OPS(sad_ops, nulldev, nulldev, sad_attach,
  88     nodev, nodev, sad_info,
  89     D_MP | D_MTPERQ | D_MTOUTPERIM | D_MTOCEXCL, &sadinfo,
  90     ddi_quiesce_not_supported);
  91 
  92 /*
  93  * Module linkage information for the kernel.
  94  */
  95 
  96 static struct modldrv modldrv = {
  97         &mod_driverops, /* Type of module.  This one is a pseudo driver */
  98         "STREAMS Administrative Driver 'sad'",
  99         &sad_ops,   /* driver ops */
 100 };
 101 
 102 static struct modlinkage modlinkage = {
 103         MODREV_1, &modldrv, NULL
 104 };
 105 
 106 int
 107 _init(void)
 108 {
 109         return (mod_install(&modlinkage));
 110 }
 111 
 112 int
 113 _fini(void)
 114 {
 115         return (mod_remove(&modlinkage));
 116 }
 117 
 118 int
 119 _info(struct modinfo *modinfop)
 120 {
 121         return (mod_info(&modlinkage, modinfop));
 122 }
 123 
 124 static int
 125 sad_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 126 {
 127         int instance = ddi_get_instance(devi);
 128 
 129         if (cmd != DDI_ATTACH)
 130                 return (DDI_FAILURE);
 131 
 132         ASSERT(instance == 0);
 133         if (instance != 0)
 134                 return (DDI_FAILURE);
 135 
 136         if (ddi_create_minor_node(devi, "user", S_IFCHR,
 137             0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
 138                 return (DDI_FAILURE);
 139         }
 140         if (ddi_create_minor_node(devi, "admin", S_IFCHR,
 141             1, DDI_PSEUDO, NULL) == DDI_FAILURE) {
 142                 ddi_remove_minor_node(devi, NULL);
 143                 return (DDI_FAILURE);
 144         }
 145         sad_dip = devi;
 146         return (DDI_SUCCESS);
 147 }
 148 
 149 /* ARGSUSED */
 150 static int
 151 sad_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
 152 {
 153         int error;
 154 
 155         switch (infocmd) {
 156         case DDI_INFO_DEVT2DEVINFO:
 157                 if (sad_dip == NULL) {
 158                         error = DDI_FAILURE;
 159                 } else {
 160                         *result = sad_dip;
 161                         error = DDI_SUCCESS;
 162                 }
 163                 break;
 164         case DDI_INFO_DEVT2INSTANCE:
 165                 *result = (void *)0;
 166                 error = DDI_SUCCESS;
 167                 break;
 168         default:
 169                 error = DDI_FAILURE;
 170         }
 171         return (error);
 172 }
 173 
 174 
 175 /*
 176  * sadopen() -
 177  * Allocate a sad device.  Only one
 178  * open at a time allowed per device.
 179  */
 180 /* ARGSUSED */
 181 static int
 182 sadopen(
 183         queue_t *qp,    /* pointer to read queue */
 184         dev_t *devp,    /* major/minor device of stream */
 185         int flag,       /* file open flags */
 186         int sflag,      /* stream open flags */
 187         cred_t *credp)  /* user credentials */
 188 {
 189         int i;
 190         netstack_t *ns;
 191         str_stack_t *ss;
 192 
 193         if (sflag)              /* no longer called from clone driver */
 194                 return (EINVAL);
 195 
 196         /* Only privileged process can access ADMINDEV */
 197         if (getminor(*devp) == ADMMIN) {
 198                 int err;
 199 
 200                 err = secpolicy_sadopen(credp);
 201 
 202                 if (err != 0)
 203                         return (err);
 204         }
 205 
 206         ns = netstack_find_by_cred(credp);
 207         ASSERT(ns != NULL);
 208         ss = ns->netstack_str;
 209         ASSERT(ss != NULL);
 210 
 211         /*
 212          * Both USRMIN and ADMMIN are clone interfaces.
 213          */
 214         for (i = 0; i < ss->ss_sadcnt; i++)
 215                 if (ss->ss_saddev[i].sa_qp == NULL)
 216                         break;
 217         if (i >= ss->ss_sadcnt) {         /* no such device */
 218                 netstack_rele(ss->ss_netstack);
 219                 return (ENXIO);
 220         }
 221         switch (getminor(*devp)) {
 222         case USRMIN:                    /* mere mortal */
 223                 ss->ss_saddev[i].sa_flags = 0;
 224                 break;
 225 
 226         case ADMMIN:                    /* privileged user */
 227                 ss->ss_saddev[i].sa_flags = SADPRIV;
 228                 break;
 229 
 230         default:
 231                 netstack_rele(ss->ss_netstack);
 232                 return (EINVAL);
 233         }
 234 
 235         ss->ss_saddev[i].sa_qp = qp;
 236         ss->ss_saddev[i].sa_ss = ss;
 237         qp->q_ptr = (caddr_t)&ss->ss_saddev[i];
 238         WR(qp)->q_ptr = (caddr_t)&ss->ss_saddev[i];
 239 
 240         /*
 241          * NOTE: should the ADMMIN or USRMIN minors change
 242          * then so should the offset of 2 below
 243          * Both USRMIN and ADMMIN are clone interfaces and
 244          * therefore their minor numbers (0 and 1) are reserved.
 245          */
 246         *devp = makedevice(getemajor(*devp), i + 2);
 247         qprocson(qp);
 248         return (0);
 249 }
 250 
 251 /*
 252  * sadclose() -
 253  * Clean up the data structures.
 254  */
 255 /* ARGSUSED */
 256 static int
 257 sadclose(
 258         queue_t *qp,    /* pointer to read queue */
 259         int flag,       /* file open flags */
 260         cred_t *credp)  /* user credentials */
 261 {
 262         struct saddev *sadp;
 263 
 264         qprocsoff(qp);
 265         sadp = (struct saddev *)qp->q_ptr;
 266         sadp->sa_qp = NULL;
 267         sadp->sa_addr = NULL;
 268         netstack_rele(sadp->sa_ss->ss_netstack);
 269         sadp->sa_ss = NULL;
 270         qp->q_ptr = NULL;
 271         WR(qp)->q_ptr = NULL;
 272         return (0);
 273 }
 274 
 275 /*
 276  * sadwput() -
 277  * Write side put procedure.
 278  */
 279 static int
 280 sadwput(
 281         queue_t *qp,    /* pointer to write queue */
 282         mblk_t *mp)     /* message pointer */
 283 {
 284         struct iocblk *iocp;
 285 
 286         switch (mp->b_datap->db_type) {
 287         case M_FLUSH:
 288                 if (*mp->b_rptr & FLUSHR) {
 289                         *mp->b_rptr &= ~FLUSHW;
 290                         qreply(qp, mp);
 291                 } else
 292                         freemsg(mp);
 293                 break;
 294 
 295         case M_IOCTL:
 296                 iocp = (struct iocblk *)mp->b_rptr;
 297                 switch (SAD_CMD(iocp->ioc_cmd)) {
 298                 case SAD_CMD(SAD_SAP):
 299                 case SAD_CMD(SAD_GAP):
 300                         apush_ioctl(qp, mp);
 301                         break;
 302 
 303                 case SAD_VML:
 304                         vml_ioctl(qp, mp);
 305                         break;
 306 
 307                 default:
 308                         miocnak(qp, mp, 0, EINVAL);
 309                         break;
 310                 }
 311                 break;
 312 
 313         case M_IOCDATA:
 314                 iocp = (struct iocblk *)mp->b_rptr;
 315                 switch (SAD_CMD(iocp->ioc_cmd)) {
 316                 case SAD_CMD(SAD_SAP):
 317                 case SAD_CMD(SAD_GAP):
 318                         apush_iocdata(qp, mp);
 319                         break;
 320 
 321                 case SAD_VML:
 322                         vml_iocdata(qp, mp);
 323                         break;
 324 
 325                 default:
 326                         cmn_err(CE_WARN,
 327                             "sadwput: invalid ioc_cmd in case M_IOCDATA: %d",
 328                             iocp->ioc_cmd);
 329                         freemsg(mp);
 330                         break;
 331                 }
 332                 break;
 333 
 334         default:
 335                 freemsg(mp);
 336                 break;
 337         } /* switch (db_type) */
 338         return (0);
 339 }
 340 
 341 /*
 342  * apush_ioctl() -
 343  * Handle the M_IOCTL messages associated with
 344  * the autopush feature.
 345  */
 346 static void
 347 apush_ioctl(
 348         queue_t *qp,    /* pointer to write queue */
 349         mblk_t *mp)     /* message pointer */
 350 {
 351         struct iocblk   *iocp;
 352         struct saddev   *sadp;
 353         uint_t          size;
 354 
 355         iocp = (struct iocblk *)mp->b_rptr;
 356         if (iocp->ioc_count != TRANSPARENT) {
 357                 miocnak(qp, mp, 0, EINVAL);
 358                 return;
 359         }
 360         if (SAD_VER(iocp->ioc_cmd) > AP_VERSION) {
 361                 miocnak(qp, mp, 0, EINVAL);
 362                 return;
 363         }
 364 
 365         sadp = (struct saddev *)qp->q_ptr;
 366         switch (SAD_CMD(iocp->ioc_cmd)) {
 367         case SAD_CMD(SAD_SAP):
 368                 if (!(sadp->sa_flags & SADPRIV)) {
 369                         miocnak(qp, mp, 0, EPERM);
 370                         break;
 371                 }
 372                 /* FALLTHRU */
 373 
 374         case SAD_CMD(SAD_GAP):
 375                 sadp->sa_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr;
 376                 if (SAD_VER(iocp->ioc_cmd) == 1)
 377                         size = STRAPUSH_V1_LEN;
 378                 else
 379                         size = STRAPUSH_V0_LEN;
 380                 mcopyin(mp, (void *)GETSTRUCT, size, NULL);
 381                 qreply(qp, mp);
 382                 break;
 383 
 384         default:
 385                 ASSERT(0);
 386                 miocnak(qp, mp, 0, EINVAL);
 387                 break;
 388         } /* switch (ioc_cmd) */
 389 }
 390 
 391 /*
 392  * apush_iocdata() -
 393  * Handle the M_IOCDATA messages associated with
 394  * the autopush feature.
 395  */
 396 static void
 397 apush_iocdata(
 398         queue_t *qp,    /* pointer to write queue */
 399         mblk_t *mp)     /* message pointer */
 400 {
 401         int i, ret;
 402         struct copyresp *csp;
 403         struct strapush *sap = NULL;
 404         struct autopush *ap, *ap_tmp;
 405         struct saddev *sadp;
 406         uint_t size;
 407         dev_t dev;
 408         str_stack_t *ss;
 409 
 410         sadp = (struct saddev *)qp->q_ptr;
 411         ss = sadp->sa_ss;
 412 
 413         csp = (struct copyresp *)mp->b_rptr;
 414         if (csp->cp_rval) {  /* if there was an error */
 415                 freemsg(mp);
 416                 return;
 417         }
 418         if (mp->b_cont) {
 419                 /*
 420                  * sap needed only if mp->b_cont is set.  figure out the
 421                  * size of the expected sap structure and make sure
 422                  * enough data was supplied.
 423                  */
 424                 if (SAD_VER(csp->cp_cmd) == 1)
 425                         size = STRAPUSH_V1_LEN;
 426                 else
 427                         size = STRAPUSH_V0_LEN;
 428                 if (MBLKL(mp->b_cont) < size) {
 429                         miocnak(qp, mp, 0, EINVAL);
 430                         return;
 431                 }
 432                 sap = (struct strapush *)mp->b_cont->b_rptr;
 433                 dev = makedevice(sap->sap_major, sap->sap_minor);
 434         }
 435         switch (SAD_CMD(csp->cp_cmd)) {
 436         case SAD_CMD(SAD_SAP):
 437 
 438                 /* currently we only support one SAD_SAP command */
 439                 if (((long)csp->cp_private) != GETSTRUCT) {
 440                         cmn_err(CE_WARN,
 441                             "apush_iocdata: cp_private bad in SAD_SAP: %p",
 442                             (void *)csp->cp_private);
 443                         miocnak(qp, mp, 0, EINVAL);
 444                         return;
 445                 }
 446 
 447                 switch (sap->sap_cmd) {
 448                 default:
 449                         miocnak(qp, mp, 0, EINVAL);
 450                         return;
 451                 case SAP_ONE:
 452                 case SAP_RANGE:
 453                 case SAP_ALL:
 454                         /* allocate and initialize a new config */
 455                         ap = sad_ap_alloc();
 456                         ap->ap_common = sap->sap_common;
 457                         if (SAD_VER(csp->cp_cmd) > 0)
 458                                 ap->ap_anchor = sap->sap_anchor;
 459                         for (i = 0; i < MIN(sap->sap_npush, MAXAPUSH); i++)
 460                                 (void) strncpy(ap->ap_list[i],
 461                                     sap->sap_list[i], FMNAMESZ);
 462 
 463                         /* sanity check the request */
 464                         if (((ret = sad_ap_verify(ap)) != 0) ||
 465                             ((ret = valid_major(ap->ap_major)) != 0)) {
 466                                 sad_ap_rele(ap, ss);
 467                                 miocnak(qp, mp, 0, ret);
 468                                 return;
 469                         }
 470 
 471                         /* check for overlapping configs */
 472                         mutex_enter(&ss->ss_sad_lock);
 473                         ap_tmp = sad_ap_find(&ap->ap_common, ss);
 474                         if (ap_tmp != NULL) {
 475                                 /* already configured */
 476                                 mutex_exit(&ss->ss_sad_lock);
 477                                 sad_ap_rele(ap_tmp, ss);
 478                                 sad_ap_rele(ap, ss);
 479                                 miocnak(qp, mp, 0, EEXIST);
 480                                 return;
 481                         }
 482 
 483                         /* add the new config to our hash */
 484                         sad_ap_insert(ap, ss);
 485                         mutex_exit(&ss->ss_sad_lock);
 486                         miocack(qp, mp, 0, 0);
 487                         return;
 488 
 489                 case SAP_CLEAR:
 490                         /* sanity check the request */
 491                         if (ret = valid_major(sap->sap_major)) {
 492                                 miocnak(qp, mp, 0, ret);
 493                                 return;
 494                         }
 495 
 496                         /* search for a matching config */
 497                         if ((ap = sad_ap_find_by_dev(dev, ss)) == NULL) {
 498                                 /* no config found */
 499                                 miocnak(qp, mp, 0, ENODEV);
 500                                 return;
 501                         }
 502 
 503                         /*
 504                          * If we matched a SAP_RANGE config
 505                          * the minor passed in must match the
 506                          * beginning of the range exactly.
 507                          */
 508                         if ((ap->ap_type == SAP_RANGE) &&
 509                             (ap->ap_minor != sap->sap_minor)) {
 510                                 sad_ap_rele(ap, ss);
 511                                 miocnak(qp, mp, 0, ERANGE);
 512                                 return;
 513                         }
 514 
 515                         /*
 516                          * If we matched a SAP_ALL config
 517                          * the minor passed in must be 0.
 518                          */
 519                         if ((ap->ap_type == SAP_ALL) &&
 520                             (sap->sap_minor != 0)) {
 521                                 sad_ap_rele(ap, ss);
 522                                 miocnak(qp, mp, 0, EINVAL);
 523                                 return;
 524                         }
 525 
 526                         /*
 527                          * make sure someone else hasn't already
 528                          * removed this config from the hash.
 529                          */
 530                         mutex_enter(&ss->ss_sad_lock);
 531                         ap_tmp = sad_ap_find(&ap->ap_common, ss);
 532                         if (ap_tmp != ap) {
 533                                 mutex_exit(&ss->ss_sad_lock);
 534                                 sad_ap_rele(ap_tmp, ss);
 535                                 sad_ap_rele(ap, ss);
 536                                 miocnak(qp, mp, 0, ENODEV);
 537                                 return;
 538                         }
 539 
 540                         /* remove the config from the hash and return */
 541                         sad_ap_remove(ap, ss);
 542                         mutex_exit(&ss->ss_sad_lock);
 543 
 544                         /*
 545                          * Release thrice, once for sad_ap_find_by_dev(),
 546                          * once for sad_ap_find(), and once to free.
 547                          */
 548                         sad_ap_rele(ap, ss);
 549                         sad_ap_rele(ap, ss);
 550                         sad_ap_rele(ap, ss);
 551                         miocack(qp, mp, 0, 0);
 552                         return;
 553                 } /* switch (sap_cmd) */
 554                 /*NOTREACHED*/
 555 
 556         case SAD_CMD(SAD_GAP):
 557                 switch ((long)csp->cp_private) {
 558 
 559                 case GETSTRUCT:
 560                         /* sanity check the request */
 561                         if (ret = valid_major(sap->sap_major)) {
 562                                 miocnak(qp, mp, 0, ret);
 563                                 return;
 564                         }
 565 
 566                         /* search for a matching config */
 567                         if ((ap = sad_ap_find_by_dev(dev, ss)) == NULL) {
 568                                 /* no config found */
 569                                 miocnak(qp, mp, 0, ENODEV);
 570                                 return;
 571                         }
 572 
 573                         /* copy out the contents of the config */
 574                         sap->sap_common = ap->ap_common;
 575                         if (SAD_VER(csp->cp_cmd) > 0)
 576                                 sap->sap_anchor = ap->ap_anchor;
 577                         for (i = 0; i < ap->ap_npush; i++)
 578                                 (void) strcpy(sap->sap_list[i], ap->ap_list[i]);
 579                         for (; i < MAXAPUSH; i++)
 580                                 bzero(sap->sap_list[i], FMNAMESZ + 1);
 581 
 582                         /* release our hold on the config */
 583                         sad_ap_rele(ap, ss);
 584 
 585                         /* copyout the results */
 586                         if (SAD_VER(csp->cp_cmd) == 1)
 587                                 size = STRAPUSH_V1_LEN;
 588                         else
 589                                 size = STRAPUSH_V0_LEN;
 590 
 591                         mcopyout(mp, (void *)GETRESULT, size, sadp->sa_addr,
 592                             NULL);
 593                         qreply(qp, mp);
 594                         return;
 595                 case GETRESULT:
 596                         miocack(qp, mp, 0, 0);
 597                         return;
 598 
 599                 default:
 600                         cmn_err(CE_WARN,
 601                             "apush_iocdata: cp_private bad case SAD_GAP: %p",
 602                             (void *)csp->cp_private);
 603                         freemsg(mp);
 604                         return;
 605                 } /* switch (cp_private) */
 606                 /*NOTREACHED*/
 607         default:        /* can't happen */
 608                 ASSERT(0);
 609                 freemsg(mp);
 610                 return;
 611         } /* switch (cp_cmd) */
 612 }
 613 
 614 /*
 615  * vml_ioctl() -
 616  * Handle the M_IOCTL message associated with a request
 617  * to validate a module list.
 618  */
 619 static void
 620 vml_ioctl(
 621         queue_t *qp,    /* pointer to write queue */
 622         mblk_t *mp)     /* message pointer */
 623 {
 624         struct iocblk *iocp;
 625 
 626         iocp = (struct iocblk *)mp->b_rptr;
 627         if (iocp->ioc_count != TRANSPARENT) {
 628                 miocnak(qp, mp, 0, EINVAL);
 629                 return;
 630         }
 631         ASSERT(SAD_CMD(iocp->ioc_cmd) == SAD_VML);
 632         mcopyin(mp, (void *)GETSTRUCT,
 633             SIZEOF_STRUCT(str_list, iocp->ioc_flag), NULL);
 634         qreply(qp, mp);
 635 }
 636 
 637 /*
 638  * vml_iocdata() -
 639  * Handle the M_IOCDATA messages associated with
 640  * a request to validate a module list.
 641  */
 642 static void
 643 vml_iocdata(
 644         queue_t *qp,    /* pointer to write queue */
 645         mblk_t *mp)     /* message pointer */
 646 {
 647         long i;
 648         int     nmods;
 649         struct copyresp *csp;
 650         struct str_mlist *lp;
 651         STRUCT_HANDLE(str_list, slp);
 652         struct saddev *sadp;
 653 
 654         csp = (struct copyresp *)mp->b_rptr;
 655         if (csp->cp_rval) {  /* if there was an error */
 656                 freemsg(mp);
 657                 return;
 658         }
 659 
 660         ASSERT(SAD_CMD(csp->cp_cmd) == SAD_VML);
 661         sadp = (struct saddev *)qp->q_ptr;
 662         switch ((long)csp->cp_private) {
 663         case GETSTRUCT:
 664                 STRUCT_SET_HANDLE(slp, csp->cp_flag,
 665                     (struct str_list *)mp->b_cont->b_rptr);
 666                 nmods = STRUCT_FGET(slp, sl_nmods);
 667                 if (nmods <= 0) {
 668                         miocnak(qp, mp, 0, EINVAL);
 669                         break;
 670                 }
 671                 sadp->sa_addr = (caddr_t)(uintptr_t)nmods;
 672 
 673                 mcopyin(mp, (void *)GETLIST, nmods * sizeof (struct str_mlist),
 674                     STRUCT_FGETP(slp, sl_modlist));
 675                 qreply(qp, mp);
 676                 break;
 677 
 678         case GETLIST:
 679                 lp = (struct str_mlist *)mp->b_cont->b_rptr;
 680                 for (i = 0; i < (long)sadp->sa_addr; i++, lp++) {
 681                         lp->l_name[FMNAMESZ] = '\0';
 682                         if (fmodsw_find(lp->l_name, FMODSW_LOAD) == NULL) {
 683                                 miocack(qp, mp, 0, 1);
 684                                 return;
 685                         }
 686                 }
 687                 miocack(qp, mp, 0, 0);
 688                 break;
 689 
 690         default:
 691                 cmn_err(CE_WARN, "vml_iocdata: invalid cp_private value: %p",
 692                     (void *)csp->cp_private);
 693                 freemsg(mp);
 694                 break;
 695         } /* switch (cp_private) */
 696 }
 697 
 698 /*
 699  * Validate a major number and also verify if
 700  * it is a STREAMS device.
 701  * Return values: 0 if a valid STREAMS dev
 702  *                error code otherwise
 703  */
 704 static int
 705 valid_major(major_t major)
 706 {
 707         int ret = 0;
 708 
 709         if (etoimajor(major) == -1)
 710                 return (EINVAL);
 711 
 712         /*
 713          * attempt to load the driver 'major' and verify that
 714          * it is a STREAMS driver.
 715          */
 716         if (ddi_hold_driver(major) == NULL)
 717                 return (EINVAL);
 718 
 719         if (!STREAMSTAB(major))
 720                 ret = ENOSTR;
 721 
 722         ddi_rele_driver(major);
 723 
 724         return (ret);
 725 }