15895Syz147064 /* 25895Syz147064 * CDDL HEADER START 35895Syz147064 * 45895Syz147064 * The contents of this file are subject to the terms of the 55895Syz147064 * Common Development and Distribution License (the "License"). 65895Syz147064 * You may not use this file except in compliance with the License. 75895Syz147064 * 85895Syz147064 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 95895Syz147064 * or http://www.opensolaris.org/os/licensing. 105895Syz147064 * See the License for the specific language governing permissions 115895Syz147064 * and limitations under the License. 125895Syz147064 * 135895Syz147064 * When distributing Covered Code, include this CDDL HEADER in each 145895Syz147064 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 155895Syz147064 * If applicable, add the following below this CDDL HEADER, with the 165895Syz147064 * fields enclosed by brackets "[]" replaced with your own identifying 175895Syz147064 * information: Portions Copyright [yyyy] [name of copyright owner] 185895Syz147064 * 195895Syz147064 * CDDL HEADER END 205895Syz147064 */ 215895Syz147064 /* 225895Syz147064 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 235895Syz147064 * Use is subject to license terms. 245895Syz147064 */ 255895Syz147064 265895Syz147064 /* 275895Syz147064 * vnode ops for the /dev/net directory 285895Syz147064 * 295895Syz147064 * The lookup is based on the internal vanity naming node table. We also 305895Syz147064 * override readdir in order to delete net nodes no longer in-use. 315895Syz147064 */ 325895Syz147064 335895Syz147064 #include <sys/types.h> 345895Syz147064 #include <sys/param.h> 355895Syz147064 #include <sys/sysmacros.h> 365895Syz147064 #include <sys/sunndi.h> 375895Syz147064 #include <fs/fs_subr.h> 385895Syz147064 #include <sys/fs/dv_node.h> 395895Syz147064 #include <sys/fs/sdev_impl.h> 405895Syz147064 #include <sys/policy.h> 415895Syz147064 #include <sys/zone.h> 425895Syz147064 #include <sys/dls.h> 435895Syz147064 445895Syz147064 struct vnodeops *devnet_vnodeops; 455895Syz147064 465895Syz147064 /* 475895Syz147064 * Called by zone_walk_datalink() to see if the given link name belongs to the 485895Syz147064 * given zone. Returns 0 to continue the walk, -1 if the link name is found. 495895Syz147064 */ 505895Syz147064 static int 515895Syz147064 devnet_validate_name(const char *link, void *arg) 525895Syz147064 { 535895Syz147064 return ((strcmp(link, arg) == 0) ? -1 : 0); 545895Syz147064 } 555895Syz147064 565895Syz147064 /* 575895Syz147064 * Check if a net sdev_node is still valid - i.e. it represents a current 585895Syz147064 * network link. 595895Syz147064 * This serves two purposes 605895Syz147064 * - only valid net nodes are returned during lookup() and readdir(). 615895Syz147064 * - since net sdev_nodes are not actively destroyed when a network link 625895Syz147064 * goes away, we use the validator to do deferred cleanup i.e. when such 635895Syz147064 * nodes are encountered during subsequent lookup() and readdir(). 645895Syz147064 */ 655895Syz147064 int 665895Syz147064 devnet_validate(struct sdev_node *dv) 675895Syz147064 { 685895Syz147064 char *nm = dv->sdev_name; 695895Syz147064 datalink_id_t linkid; 705895Syz147064 715895Syz147064 ASSERT(!(dv->sdev_flags & SDEV_STALE)); 725895Syz147064 ASSERT(dv->sdev_state == SDEV_READY); 735895Syz147064 745895Syz147064 if (SDEV_IS_GLOBAL(dv)) { 755895Syz147064 return ((dls_mgmt_get_linkid(nm, &linkid) != 0) ? 765895Syz147064 SDEV_VTOR_INVALID : SDEV_VTOR_VALID); 775895Syz147064 } else { 785895Syz147064 return ((zone_datalink_walk(getzoneid(), devnet_validate_name, 795895Syz147064 nm) == -1) ? SDEV_VTOR_VALID : SDEV_VTOR_INVALID); 805895Syz147064 } 815895Syz147064 } 825895Syz147064 835895Syz147064 /* 845895Syz147064 * This callback is invoked from devname_lookup_func() to create 855895Syz147064 * a net entry when the node is not found in the cache. 865895Syz147064 */ 875895Syz147064 static int 885895Syz147064 devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp) 895895Syz147064 { 905895Syz147064 timestruc_t now; 915895Syz147064 dev_t dev; 925895Syz147064 int error; 935895Syz147064 945895Syz147064 if ((error = dls_devnet_open(nm, ddhp, &dev)) != 0) { 955895Syz147064 sdcmn_err12(("devnet_create_rvp: not a valid vanity name " 965895Syz147064 "network node: %s\n", nm)); 975895Syz147064 return (error); 985895Syz147064 } 995895Syz147064 1005895Syz147064 /* 1015895Syz147064 * This is a valid network device (at least at this point in time). 1025895Syz147064 * Create the node by setting the attribute; the rest is taken care 1035895Syz147064 * of by devname_lookup_func(). 1045895Syz147064 */ 1055895Syz147064 *vap = sdev_vattr_chr; 1065895Syz147064 vap->va_mode |= 0666; 1075895Syz147064 vap->va_rdev = dev; 1085895Syz147064 1095895Syz147064 gethrestime(&now); 1105895Syz147064 vap->va_atime = now; 1115895Syz147064 vap->va_mtime = now; 1125895Syz147064 vap->va_ctime = now; 1135895Syz147064 return (0); 1145895Syz147064 } 1155895Syz147064 1165895Syz147064 /* 1175895Syz147064 * Lookup for /dev/net directory 1185895Syz147064 * If the entry does not exist, the devnet_create_rvp() callback 1195895Syz147064 * is invoked to create it. Nodes do not persist across reboot. 1205895Syz147064 */ 1215895Syz147064 /*ARGSUSED3*/ 1225895Syz147064 static int 1235895Syz147064 devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 1245895Syz147064 struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, 1255895Syz147064 caller_context_t *ct, int *direntflags, pathname_t *realpnp) 1265895Syz147064 { 1275895Syz147064 struct sdev_node *ddv = VTOSDEV(dvp); 1285895Syz147064 struct sdev_node *dv = NULL; 1295895Syz147064 dls_dl_handle_t ddh = NULL; 1305895Syz147064 struct vattr vattr; 1315895Syz147064 int nmlen; 1325895Syz147064 int error = ENOENT; 1335895Syz147064 1345895Syz147064 if (SDEVTOV(ddv)->v_type != VDIR) 1355895Syz147064 return (ENOTDIR); 1365895Syz147064 1375895Syz147064 /* 1385895Syz147064 * Empty name or ., return node itself. 1395895Syz147064 */ 1405895Syz147064 nmlen = strlen(nm); 1415895Syz147064 if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 1425895Syz147064 *vpp = SDEVTOV(ddv); 1435895Syz147064 VN_HOLD(*vpp); 1445895Syz147064 return (0); 1455895Syz147064 } 1465895Syz147064 1475895Syz147064 /* 1485895Syz147064 * .., return the parent directory 1495895Syz147064 */ 1505895Syz147064 if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 1515895Syz147064 *vpp = SDEVTOV(ddv->sdev_dotdot); 1525895Syz147064 VN_HOLD(*vpp); 1535895Syz147064 return (0); 1545895Syz147064 } 1555895Syz147064 1565895Syz147064 rw_enter(&ddv->sdev_contents, RW_WRITER); 1575895Syz147064 1585895Syz147064 /* 1595895Syz147064 * directory cache lookup: 1605895Syz147064 */ 1615895Syz147064 if ((dv = sdev_cache_lookup(ddv, nm)) != NULL) { 1625895Syz147064 if (dv->sdev_state == SDEV_READY) { 1635895Syz147064 if (!(dv->sdev_flags & SDEV_ATTR_INVALID)) 1645895Syz147064 goto found; 1655895Syz147064 } else { 1665895Syz147064 ASSERT(dv->sdev_state == SDEV_ZOMBIE); 1675895Syz147064 goto failed; 1685895Syz147064 } 1695895Syz147064 } 1705895Syz147064 1715895Syz147064 /* 1725895Syz147064 * ZOMBIED parent does not allow new node creation, bail out early. 1735895Syz147064 */ 1745895Syz147064 if (ddv->sdev_state == SDEV_ZOMBIE) 1755895Syz147064 goto failed; 1765895Syz147064 1775895Syz147064 error = devnet_create_rvp(nm, &vattr, &ddh); 1785895Syz147064 if (error != 0) 1795895Syz147064 goto failed; 1805895Syz147064 1815895Syz147064 error = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, cred, SDEV_READY); 1825895Syz147064 if (error != 0) { 1835895Syz147064 ASSERT(dv == NULL); 1845895Syz147064 dls_devnet_close(ddh); 1855895Syz147064 goto failed; 1865895Syz147064 } 1875895Syz147064 1885895Syz147064 ASSERT(dv != NULL); 1895895Syz147064 1905895Syz147064 rw_enter(&dv->sdev_contents, RW_WRITER); 1915895Syz147064 if (dv->sdev_flags & SDEV_ATTR_INVALID) { 1925895Syz147064 /* 1935895Syz147064 * SDEV_ATTR_INVALID means that this device has been 1945895Syz147064 * detached, and its dev_t might've been changed too. 1955895Syz147064 * Therefore, sdev_node's 'vattr' needs to be updated. 1965895Syz147064 */ 1975895Syz147064 SDEVTOV(dv)->v_rdev = vattr.va_rdev; 1985895Syz147064 ASSERT(dv->sdev_attr != NULL); 1995895Syz147064 dv->sdev_attr->va_rdev = vattr.va_rdev; 2005895Syz147064 dv->sdev_flags &= ~SDEV_ATTR_INVALID; 2015895Syz147064 } 2025895Syz147064 ASSERT(dv->sdev_private == NULL); 2035895Syz147064 dv->sdev_private = ddh; 2045895Syz147064 rw_exit(&dv->sdev_contents); 2055895Syz147064 2065895Syz147064 found: 2075895Syz147064 ASSERT(SDEV_HELD(dv)); 2085895Syz147064 rw_exit(&ddv->sdev_contents); 2095895Syz147064 return (sdev_to_vp(dv, vpp)); 2105895Syz147064 2115895Syz147064 failed: 2125895Syz147064 rw_exit(&ddv->sdev_contents); 2135895Syz147064 2145895Syz147064 if (dv != NULL) 2155895Syz147064 SDEV_RELE(dv); 2165895Syz147064 2175895Syz147064 *vpp = NULL; 2185895Syz147064 return (error); 2195895Syz147064 } 2205895Syz147064 2215895Syz147064 static int 2225895Syz147064 devnet_filldir_datalink(const char *link, void *arg) 2235895Syz147064 { 2245895Syz147064 struct sdev_node *ddv = arg; 2255895Syz147064 struct vattr vattr; 2265895Syz147064 struct sdev_node *dv; 2275895Syz147064 dls_dl_handle_t ddh = NULL; 2285895Syz147064 2295895Syz147064 ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 2305895Syz147064 if ((dv = sdev_cache_lookup(ddv, (char *)link)) != NULL) 2315895Syz147064 goto found; 2325895Syz147064 2335895Syz147064 if (devnet_create_rvp(link, &vattr, &ddh) != 0) 2345895Syz147064 return (0); 2355895Syz147064 2365895Syz147064 ASSERT(ddh != NULL); 2375895Syz147064 dls_devnet_close(ddh); 2385895Syz147064 2395895Syz147064 if (sdev_mknode(ddv, (char *)link, &dv, &vattr, NULL, NULL, kcred, 2405895Syz147064 SDEV_READY) != 0) { 2415895Syz147064 return (0); 2425895Syz147064 } 2435895Syz147064 2445895Syz147064 /* 2455895Syz147064 * As there is no reference holding the network device, it could be 2465895Syz147064 * detached. Set SDEV_ATTR_INVALID so that the 'vattr' will be updated 2475895Syz147064 * later. 2485895Syz147064 */ 2495895Syz147064 rw_enter(&dv->sdev_contents, RW_WRITER); 2505895Syz147064 dv->sdev_flags |= SDEV_ATTR_INVALID; 2515895Syz147064 rw_exit(&dv->sdev_contents); 2525895Syz147064 2535895Syz147064 found: 2545895Syz147064 SDEV_SIMPLE_RELE(dv); 2555895Syz147064 return (0); 2565895Syz147064 } 2575895Syz147064 2585895Syz147064 static void 2595895Syz147064 devnet_filldir(struct sdev_node *ddv) 2605895Syz147064 { 2615895Syz147064 sdev_node_t *dv, *next; 2625895Syz147064 char link[MAXLINKNAMELEN]; 2635895Syz147064 datalink_id_t linkid; 2645895Syz147064 2655895Syz147064 ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 2665895Syz147064 if (rw_tryupgrade(&ddv->sdev_contents) == NULL) { 2675895Syz147064 rw_exit(&ddv->sdev_contents); 2685895Syz147064 rw_enter(&ddv->sdev_contents, RW_WRITER); 2695895Syz147064 } 2705895Syz147064 2716260Sjg for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) { 2726260Sjg next = SDEV_NEXT_ENTRY(ddv, dv); 2735895Syz147064 2745895Syz147064 /* validate and prune only ready nodes */ 2755895Syz147064 if (dv->sdev_state != SDEV_READY) 2765895Syz147064 continue; 2775895Syz147064 2785895Syz147064 switch (devnet_validate(dv)) { 2795895Syz147064 case SDEV_VTOR_VALID: 2805895Syz147064 case SDEV_VTOR_SKIP: 2815895Syz147064 continue; 2825895Syz147064 case SDEV_VTOR_INVALID: 283*8023SPhil.Kirk@Sun.COM case SDEV_VTOR_STALE: 2845895Syz147064 sdcmn_err12(("devnet_filldir: destroy invalid " 2855895Syz147064 "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 2865895Syz147064 break; 2875895Syz147064 } 2885895Syz147064 2895895Syz147064 if (SDEVTOV(dv)->v_count > 0) 2905895Syz147064 continue; 2915895Syz147064 SDEV_HOLD(dv); 2925895Syz147064 /* remove the cache node */ 2935895Syz147064 (void) sdev_cache_update(ddv, &dv, dv->sdev_name, 2945895Syz147064 SDEV_CACHE_DELETE); 2955895Syz147064 } 2965895Syz147064 2975895Syz147064 if (((ddv->sdev_flags & SDEV_BUILD) == 0) && !dls_devnet_rebuild()) 2985895Syz147064 goto done; 2995895Syz147064 3005895Syz147064 if (SDEV_IS_GLOBAL(ddv)) { 3015895Syz147064 linkid = DATALINK_INVALID_LINKID; 3025895Syz147064 do { 3035895Syz147064 linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL, 3045895Syz147064 DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE); 3055895Syz147064 3065895Syz147064 if ((linkid != DATALINK_INVALID_LINKID) && 3075895Syz147064 (dls_mgmt_get_linkinfo(linkid, link, 3085895Syz147064 NULL, NULL, NULL) == 0)) { 3095895Syz147064 (void) devnet_filldir_datalink(link, ddv); 3105895Syz147064 } 3115895Syz147064 } while (linkid != DATALINK_INVALID_LINKID); 3125895Syz147064 } else { 3135895Syz147064 (void) zone_datalink_walk(getzoneid(), 3145895Syz147064 devnet_filldir_datalink, ddv); 3155895Syz147064 } 3165895Syz147064 3175895Syz147064 ddv->sdev_flags &= ~SDEV_BUILD; 3185895Syz147064 3195895Syz147064 done: 3205895Syz147064 rw_downgrade(&ddv->sdev_contents); 3215895Syz147064 } 3225895Syz147064 3235895Syz147064 /* 3245895Syz147064 * Display all instantiated network datalink device nodes. 3255895Syz147064 * A /dev/net entry will be created only after the first lookup of 3265895Syz147064 * the network datalink device succeeds. 3275895Syz147064 */ 3285895Syz147064 /*ARGSUSED4*/ 3295895Syz147064 static int 3305895Syz147064 devnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, 3315895Syz147064 int *eofp, caller_context_t *ct, int flags) 3325895Syz147064 { 3335895Syz147064 struct sdev_node *sdvp = VTOSDEV(dvp); 3345895Syz147064 3355895Syz147064 ASSERT(sdvp); 3365895Syz147064 3375895Syz147064 if (uiop->uio_offset == 0) 3385895Syz147064 devnet_filldir(sdvp); 3395895Syz147064 3405895Syz147064 return (devname_readdir_func(dvp, uiop, cred, eofp, 0)); 3415895Syz147064 } 3425895Syz147064 3435895Syz147064 /* 3445895Syz147064 * This callback is invoked from devname_inactive_func() to release 3455895Syz147064 * the net entry which was held in devnet_create_rvp(). 3465895Syz147064 */ 3475895Syz147064 static void 3485895Syz147064 devnet_inactive_callback(struct vnode *dvp) 3495895Syz147064 { 3505895Syz147064 struct sdev_node *sdvp = VTOSDEV(dvp); 3515895Syz147064 dls_dl_handle_t ddh; 3525895Syz147064 3535895Syz147064 if (dvp->v_type == VDIR) 3545895Syz147064 return; 3555895Syz147064 3565895Syz147064 ASSERT(dvp->v_type == VCHR); 3575895Syz147064 rw_enter(&sdvp->sdev_contents, RW_WRITER); 3585895Syz147064 ddh = sdvp->sdev_private; 3595895Syz147064 sdvp->sdev_private = NULL; 3605895Syz147064 sdvp->sdev_flags |= SDEV_ATTR_INVALID; 3615895Syz147064 rw_exit(&sdvp->sdev_contents); 3625895Syz147064 3635895Syz147064 /* 3645895Syz147064 * "ddh" (sdev_private) could be NULL if devnet_lookup fails. 3655895Syz147064 */ 3665895Syz147064 if (ddh != NULL) 3675895Syz147064 dls_devnet_close(ddh); 3685895Syz147064 } 3695895Syz147064 3705895Syz147064 /*ARGSUSED*/ 3715895Syz147064 static void 3725895Syz147064 devnet_inactive(struct vnode *dvp, struct cred *cred, caller_context_t *ct) 3735895Syz147064 { 3745895Syz147064 devname_inactive_func(dvp, cred, devnet_inactive_callback); 3755895Syz147064 } 3765895Syz147064 3775895Syz147064 /* 3785895Syz147064 * We override lookup and readdir to build entries based on the 3795895Syz147064 * in kernel vanity naming node table. 3805895Syz147064 */ 3815895Syz147064 const fs_operation_def_t devnet_vnodeops_tbl[] = { 3825895Syz147064 VOPNAME_READDIR, { .vop_readdir = devnet_readdir }, 3835895Syz147064 VOPNAME_LOOKUP, { .vop_lookup = devnet_lookup }, 3845895Syz147064 VOPNAME_INACTIVE, { .vop_inactive = devnet_inactive }, 3855895Syz147064 VOPNAME_CREATE, { .error = fs_nosys }, 3865895Syz147064 VOPNAME_REMOVE, { .error = fs_nosys }, 3875895Syz147064 VOPNAME_MKDIR, { .error = fs_nosys }, 3885895Syz147064 VOPNAME_RMDIR, { .error = fs_nosys }, 3895895Syz147064 VOPNAME_SYMLINK, { .error = fs_nosys }, 3905895Syz147064 VOPNAME_SETSECATTR, { .error = fs_nosys }, 3915895Syz147064 NULL, NULL 3925895Syz147064 }; 393