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