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