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