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