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 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * vnode ops for the /dev/net directory
  28  *
  29  *      The lookup is based on the internal vanity naming node table.  We also
  30  *      override readdir in order to delete net nodes no longer in-use.
  31  */
  32 
  33 #include <sys/types.h>
  34 #include <sys/param.h>
  35 #include <sys/sysmacros.h>
  36 #include <sys/sunndi.h>
  37 #include <fs/fs_subr.h>
  38 #include <sys/fs/dv_node.h>
  39 #include <sys/fs/sdev_impl.h>
  40 #include <sys/policy.h>
  41 #include <sys/zone.h>
  42 #include <sys/dls.h>
  43 
  44 struct vnodeops         *devnet_vnodeops;
  45 
  46 /*
  47  * Check if a net sdev_node is still valid - i.e. it represents a current
  48  * network link.
  49  * This serves two purposes
  50  *      - only valid net nodes are returned during lookup() and readdir().
  51  *      - since net sdev_nodes are not actively destroyed when a network link
  52  *        goes away, we use the validator to do deferred cleanup i.e. when such
  53  *        nodes are encountered during subsequent lookup() and readdir().
  54  */
  55 int
  56 devnet_validate(struct sdev_node *dv)
  57 {
  58         datalink_id_t linkid;
  59         zoneid_t zoneid;
  60 
  61         ASSERT(dv->sdev_state == SDEV_READY);
  62 
  63         if (dls_mgmt_get_linkid(dv->sdev_name, &linkid) != 0)
  64                 return (SDEV_VTOR_INVALID);
  65         if (SDEV_IS_GLOBAL(dv))
  66                 return (SDEV_VTOR_VALID);
  67         zoneid = getzoneid();
  68         return (zone_check_datalink(&zoneid, linkid) == 0 ?
  69             SDEV_VTOR_VALID : SDEV_VTOR_INVALID);
  70 }
  71 
  72 /*
  73  * This callback is invoked from devname_lookup_func() to create
  74  * a net entry when the node is not found in the cache.
  75  */
  76 static int
  77 devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp)
  78 {
  79         timestruc_t now;
  80         dev_t dev;
  81         int error;
  82 
  83         if ((error = dls_devnet_open(nm, ddhp, &dev)) != 0) {
  84                 sdcmn_err12(("devnet_create_rvp: not a valid vanity name "
  85                     "network node: %s\n", nm));
  86                 return (error);
  87         }
  88 
  89         /*
  90          * This is a valid network device (at least at this point in time).
  91          * Create the node by setting the attribute; the rest is taken care
  92          * of by devname_lookup_func().
  93          */
  94         *vap = sdev_vattr_chr;
  95         vap->va_mode |= 0666;
  96         vap->va_rdev = dev;
  97 
  98         gethrestime(&now);
  99         vap->va_atime = now;
 100         vap->va_mtime = now;
 101         vap->va_ctime = now;
 102         return (0);
 103 }
 104 
 105 /*
 106  * Lookup for /dev/net directory
 107  *      If the entry does not exist, the devnet_create_rvp() callback
 108  *      is invoked to create it.  Nodes do not persist across reboot.
 109  */
 110 /*ARGSUSED3*/
 111 static int
 112 devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
 113     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
 114     caller_context_t *ct, int *direntflags, pathname_t *realpnp)
 115 {
 116         struct sdev_node *ddv = VTOSDEV(dvp);
 117         struct sdev_node *dv = NULL;
 118         dls_dl_handle_t ddh = NULL;
 119         struct vattr vattr;
 120         int nmlen;
 121         int error = ENOENT;
 122 
 123         if (SDEVTOV(ddv)->v_type != VDIR)
 124                 return (ENOTDIR);
 125 
 126         /*
 127          * Empty name or ., return node itself.
 128          */
 129         nmlen = strlen(nm);
 130         if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
 131                 *vpp = SDEVTOV(ddv);
 132                 VN_HOLD(*vpp);
 133                 return (0);
 134         }
 135 
 136         /*
 137          * .., return the parent directory
 138          */
 139         if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
 140                 *vpp = SDEVTOV(ddv->sdev_dotdot);
 141                 VN_HOLD(*vpp);
 142                 return (0);
 143         }
 144 
 145         rw_enter(&ddv->sdev_contents, RW_WRITER);
 146 
 147         /*
 148          * directory cache lookup:
 149          */
 150         if ((dv = sdev_cache_lookup(ddv, nm)) != NULL) {
 151                 ASSERT(dv->sdev_state == SDEV_READY);
 152                 if (!(dv->sdev_flags & SDEV_ATTR_INVALID))
 153                         goto found;
 154         }
 155 
 156         /*
 157          * ZOMBIED parent does not allow new node creation, bail out early.
 158          */
 159         if (ddv->sdev_state == SDEV_ZOMBIE)
 160                 goto failed;
 161 
 162         error = devnet_create_rvp(nm, &vattr, &ddh);
 163         if (error != 0)
 164                 goto failed;
 165 
 166         error = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, cred, SDEV_READY);
 167         if (error != 0) {
 168                 dls_devnet_close(ddh);
 169                 goto failed;
 170         }
 171 
 172         ASSERT(dv != NULL);
 173 
 174         rw_enter(&dv->sdev_contents, RW_WRITER);
 175         if (dv->sdev_flags & SDEV_ATTR_INVALID) {
 176                 /*
 177                  * SDEV_ATTR_INVALID means that this device has been
 178                  * detached, and its dev_t might've been changed too.
 179                  * Therefore, sdev_node's 'vattr' needs to be updated.
 180                  */
 181                 SDEVTOV(dv)->v_rdev = vattr.va_rdev;
 182                 ASSERT(dv->sdev_attr != NULL);
 183                 dv->sdev_attr->va_rdev = vattr.va_rdev;
 184                 dv->sdev_flags &= ~SDEV_ATTR_INVALID;
 185         }
 186         ASSERT(dv->sdev_private == NULL);
 187         dv->sdev_private = ddh;
 188         rw_exit(&dv->sdev_contents);
 189 
 190 found:
 191         ASSERT(SDEV_HELD(dv));
 192         rw_exit(&ddv->sdev_contents);
 193         return (sdev_to_vp(dv, vpp));
 194 
 195 failed:
 196         rw_exit(&ddv->sdev_contents);
 197 
 198         if (dv != NULL)
 199                 SDEV_RELE(dv);
 200 
 201         *vpp = NULL;
 202         return (error);
 203 }
 204 
 205 static int
 206 devnet_filldir_datalink(datalink_id_t linkid, void *arg)
 207 {
 208         struct sdev_node        *ddv = arg;
 209         struct vattr            vattr;
 210         struct sdev_node        *dv;
 211         dls_dl_handle_t         ddh = NULL;
 212         char                    link[MAXLINKNAMELEN];
 213 
 214         ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
 215 
 216         if (dls_mgmt_get_linkinfo(linkid, link, NULL, NULL, NULL) != 0)
 217                 return (0);
 218 
 219         if ((dv = sdev_cache_lookup(ddv, (char *)link)) != NULL)
 220                 goto found;
 221 
 222         if (devnet_create_rvp(link, &vattr, &ddh) != 0)
 223                 return (0);
 224 
 225         ASSERT(ddh != NULL);
 226         dls_devnet_close(ddh);
 227 
 228         if (sdev_mknode(ddv, (char *)link, &dv, &vattr, NULL, NULL, kcred,
 229             SDEV_READY) != 0) {
 230                 return (0);
 231         }
 232 
 233         /*
 234          * As there is no reference holding the network device, it could be
 235          * detached. Set SDEV_ATTR_INVALID so that the 'vattr' will be updated
 236          * later.
 237          */
 238         rw_enter(&dv->sdev_contents, RW_WRITER);
 239         dv->sdev_flags |= SDEV_ATTR_INVALID;
 240         rw_exit(&dv->sdev_contents);
 241 
 242 found:
 243         SDEV_SIMPLE_RELE(dv);
 244         return (0);
 245 }
 246 
 247 static void
 248 devnet_filldir(struct sdev_node *ddv)
 249 {
 250         sdev_node_t     *dv, *next;
 251         datalink_id_t   linkid;
 252 
 253         ASSERT(RW_READ_HELD(&ddv->sdev_contents));
 254         if (rw_tryupgrade(&ddv->sdev_contents) == NULL) {
 255                 rw_exit(&ddv->sdev_contents);
 256                 rw_enter(&ddv->sdev_contents, RW_WRITER);
 257         }
 258 
 259         for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
 260                 next = SDEV_NEXT_ENTRY(ddv, dv);
 261 
 262                 /* validate and prune only ready nodes */
 263                 if (dv->sdev_state != SDEV_READY)
 264                         continue;
 265 
 266                 switch (devnet_validate(dv)) {
 267                 case SDEV_VTOR_VALID:
 268                 case SDEV_VTOR_SKIP:
 269                         continue;
 270                 case SDEV_VTOR_INVALID:
 271                 case SDEV_VTOR_STALE:
 272                         sdcmn_err12(("devnet_filldir: destroy invalid "
 273                             "node: %s(%p)\n", dv->sdev_name, (void *)dv));
 274                         break;
 275                 }
 276 
 277                 if (SDEVTOV(dv)->v_count > 0)
 278                         continue;
 279                 SDEV_HOLD(dv);
 280                 /* remove the cache node */
 281                 (void) sdev_cache_update(ddv, &dv, dv->sdev_name,
 282                     SDEV_CACHE_DELETE);
 283                 SDEV_RELE(dv);
 284         }
 285 
 286         if (((ddv->sdev_flags & SDEV_BUILD) == 0) && !dls_devnet_rebuild())
 287                 goto done;
 288 
 289         if (SDEV_IS_GLOBAL(ddv)) {
 290                 linkid = DATALINK_INVALID_LINKID;
 291                 do {
 292                         linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL,
 293                             DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE);
 294                         if (linkid != DATALINK_INVALID_LINKID)
 295                                 (void) devnet_filldir_datalink(linkid, ddv);
 296                 } while (linkid != DATALINK_INVALID_LINKID);
 297         } else {
 298                 (void) zone_datalink_walk(getzoneid(),
 299                     devnet_filldir_datalink, ddv);
 300         }
 301 
 302         ddv->sdev_flags &= ~SDEV_BUILD;
 303 
 304 done:
 305         rw_downgrade(&ddv->sdev_contents);
 306 }
 307 
 308 /*
 309  * Display all instantiated network datalink device nodes.
 310  * A /dev/net entry will be created only after the first lookup of
 311  * the network datalink device succeeds.
 312  */
 313 /*ARGSUSED4*/
 314 static int
 315 devnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
 316     int *eofp, caller_context_t *ct, int flags)
 317 {
 318         struct sdev_node *sdvp = VTOSDEV(dvp);
 319 
 320         ASSERT(sdvp);
 321 
 322         if (uiop->uio_offset == 0)
 323                 devnet_filldir(sdvp);
 324 
 325         return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
 326 }
 327 
 328 /*
 329  * This callback is invoked from devname_inactive_func() to release
 330  * the net entry which was held in devnet_create_rvp().
 331  */
 332 static void
 333 devnet_inactive_callback(struct vnode *dvp)
 334 {
 335         struct sdev_node *sdvp = VTOSDEV(dvp);
 336         dls_dl_handle_t ddh;
 337 
 338         if (dvp->v_type == VDIR)
 339                 return;
 340 
 341         ASSERT(dvp->v_type == VCHR);
 342         rw_enter(&sdvp->sdev_contents, RW_WRITER);
 343         ddh = sdvp->sdev_private;
 344         sdvp->sdev_private = NULL;
 345         sdvp->sdev_flags |= SDEV_ATTR_INVALID;
 346         rw_exit(&sdvp->sdev_contents);
 347 
 348         /*
 349          * "ddh" (sdev_private) could be NULL if devnet_lookup fails.
 350          */
 351         if (ddh != NULL)
 352                 dls_devnet_close(ddh);
 353 }
 354 
 355 /*ARGSUSED*/
 356 static void
 357 devnet_inactive(struct vnode *dvp, struct cred *cred, caller_context_t *ct)
 358 {
 359         devname_inactive_func(dvp, cred, devnet_inactive_callback);
 360 }
 361 
 362 /*
 363  * We override lookup and readdir to build entries based on the
 364  * in kernel vanity naming node table.
 365  */
 366 const fs_operation_def_t devnet_vnodeops_tbl[] = {
 367         VOPNAME_READDIR,        { .vop_readdir = devnet_readdir },
 368         VOPNAME_LOOKUP,         { .vop_lookup = devnet_lookup },
 369         VOPNAME_INACTIVE,       { .vop_inactive = devnet_inactive },
 370         VOPNAME_CREATE,         { .error = fs_nosys },
 371         VOPNAME_REMOVE,         { .error = fs_nosys },
 372         VOPNAME_MKDIR,          { .error = fs_nosys },
 373         VOPNAME_RMDIR,          { .error = fs_nosys },
 374         VOPNAME_SYMLINK,        { .error = fs_nosys },
 375         VOPNAME_SETSECATTR,     { .error = fs_nosys },
 376         NULL,                   NULL
 377 };