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