xref: /illumos-gate/usr/src/cmd/dlmgmtd/dlmgmt_door.c (revision 24b9abbad58fdd63dad716fd35a99a7944c4e3eb)
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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Main door handler functions used by dlmgmtd to process the different door
31  * call requests. Door call requests can come from the user-land applications,
32  * or from the kernel.
33  */
34 
35 #include <assert.h>
36 #include <alloca.h>
37 #include <errno.h>
38 #include <priv_utils.h>
39 #include <stdlib.h>
40 #include <strings.h>
41 #include <libdlmgmt.h>
42 #include "dlmgmt_impl.h"
43 
44 typedef void dlmgmt_door_handler_t(void *, void *);
45 
46 typedef struct dlmgmt_door_info_s {
47 	uint_t			di_cmd;
48 	boolean_t		di_set;
49 	size_t			di_reqsz;
50 	size_t			di_acksz;
51 	dlmgmt_door_handler_t	*di_handler;
52 } dlmgmt_door_info_t;
53 
54 
55 static dlmgmt_link_t *
56 dlmgmt_getlink_by_dev(char *devname)
57 {
58 	dlmgmt_link_t *linkp = avl_first(&dlmgmt_id_avl);
59 
60 	for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
61 		if ((linkp->ll_class == DATALINK_CLASS_PHYS) &&
62 		    linkattr_equal(&(linkp->ll_head), FDEVNAME, devname,
63 		    strlen(devname) + 1)) {
64 			return (linkp);
65 		}
66 	}
67 	return (NULL);
68 }
69 
70 static void
71 dlmgmt_upcall_create(void *argp, void *retp)
72 {
73 	dlmgmt_upcall_arg_create_t *create = argp;
74 	dlmgmt_create_retval_t	*retvalp = retp;
75 	datalink_class_t	class;
76 	uint32_t		media;
77 	dlmgmt_link_t		*linkp;
78 	char			link[MAXLINKNAMELEN];
79 	uint32_t		flags;
80 	int			err;
81 	boolean_t		created = B_FALSE;
82 
83 	/*
84 	 * Determine whether this link is persistent. Note that this request
85 	 * is coming from kernel so this link must be active.
86 	 */
87 	flags = DLMGMT_ACTIVE | (create->ld_persist ? DLMGMT_PERSIST : 0);
88 
89 	class = create->ld_class;
90 	media = create->ld_media;
91 
92 	/*
93 	 * Hold the writer lock to update the link table.
94 	 */
95 	dlmgmt_table_lock(B_TRUE);
96 
97 	/*
98 	 * Check to see whether this is the reattachment of an existing
99 	 * physical link. If so, return its linkid.
100 	 */
101 	if ((class == DATALINK_CLASS_PHYS) &&
102 	    (linkp = dlmgmt_getlink_by_dev(create->ld_devname)) != NULL) {
103 		err = linkattr_set(&(linkp->ll_head), FPHYMAJ,
104 		    &create->ld_phymaj, sizeof (uint64_t), DLADM_TYPE_UINT64);
105 		if (err != 0)
106 			goto done;
107 
108 		err = linkattr_set(&(linkp->ll_head), FPHYINST,
109 		    &create->ld_phyinst, sizeof (uint64_t), DLADM_TYPE_UINT64);
110 		if (err != 0)
111 			goto done;
112 
113 		linkp->ll_flags |= flags;
114 		linkp->ll_gen++;
115 		goto done;
116 	}
117 
118 	if ((err = dlmgmt_create_common(create->ld_devname, class, media,
119 	    flags, &linkp)) == EEXIST) {
120 		/*
121 		 * The link name already exists. Return error if this is a
122 		 * non-physical link (in that case, the link name must be
123 		 * the same as the given name).
124 		 */
125 		if (class != DATALINK_CLASS_PHYS)
126 			goto done;
127 
128 		/*
129 		 * The physical link's name already exists, request
130 		 * a suggested link name: net<nextppa>
131 		 */
132 		err = dlmgmt_generate_name("net", link, MAXLINKNAMELEN);
133 		if (err != 0)
134 			goto done;
135 
136 		err = dlmgmt_create_common(link, class, media, flags, &linkp);
137 	}
138 
139 	if (err != 0)
140 		goto done;
141 
142 	created = B_TRUE;
143 
144 	/*
145 	 * This is a new link.  Only need to persist link attributes for
146 	 * physical links.
147 	 */
148 	if (class == DATALINK_CLASS_PHYS &&
149 	    (((err = linkattr_set(&linkp->ll_head, FDEVNAME, create->ld_devname,
150 	    strlen(create->ld_devname) + 1, DLADM_TYPE_STR)) != 0) ||
151 	    ((err = linkattr_set(&linkp->ll_head, FPHYMAJ, &create->ld_phymaj,
152 	    sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0) ||
153 	    ((err = linkattr_set(&linkp->ll_head, FPHYINST, &create->ld_phyinst,
154 	    sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0))) {
155 		(void) dlmgmt_destroy_common(linkp, flags);
156 		goto done;
157 	}
158 
159 done:
160 	if ((err == 0) && ((err = dlmgmt_write_db_entry(linkp->ll_linkid,
161 	    linkp->ll_flags)) != 0) && created) {
162 		(void) dlmgmt_destroy_common(linkp, flags);
163 	}
164 
165 	if (err == 0)
166 		retvalp->lr_linkid = linkp->ll_linkid;
167 
168 	retvalp->lr_err = err;
169 	dlmgmt_table_unlock();
170 }
171 
172 static void
173 dlmgmt_upcall_update(void *argp, void *retp)
174 {
175 	dlmgmt_upcall_arg_update_t	*update = argp;
176 	dlmgmt_update_retval_t		*retvalp = retp;
177 	uint32_t			media = update->ld_media;
178 	dlmgmt_link_t			*linkp;
179 	int				err = 0;
180 
181 	/*
182 	 * Hold the writer lock to update the link table.
183 	 */
184 	dlmgmt_table_lock(B_TRUE);
185 
186 	/*
187 	 * Check to see whether this is the reattachment of an existing
188 	 * physical link. If so, return its linkid.
189 	 */
190 	if ((linkp = dlmgmt_getlink_by_dev(update->ld_devname)) == NULL) {
191 		err = ENOENT;
192 		goto done;
193 	}
194 
195 	retvalp->lr_linkid = linkp->ll_linkid;
196 	retvalp->lr_media = media;
197 	if (linkp->ll_media != media && linkp->ll_media != DL_OTHER) {
198 		/*
199 		 * Assume a DL_ETHER link ce0, a DL_WIFI link ath0
200 		 * 1. # dladm rename-link ce0 net0
201 		 * 2. DR out ce0. net0 is down.
202 		 * 3. use rename-link to have the ath0 device inherit
203 		 *    the configuration from net0
204 		 *    # dladm rename-link ath0 net0
205 		 * 4. DR in ath0.
206 		 * As ath0 and ce0 do not have the same media type, ath0
207 		 * cannot inherit the configuration of net0.
208 		 */
209 		err = EEXIST;
210 
211 		/*
212 		 * Return the media type of the existing link to indicate the
213 		 * reason for the name conflict.
214 		 */
215 		retvalp->lr_media = linkp->ll_media;
216 		goto done;
217 	}
218 
219 	if (update->ld_novanity &&
220 	    (strcmp(update->ld_devname, linkp->ll_link) != 0)) {
221 		/*
222 		 * Return an error if this is a physical link that does not
223 		 * support vanity naming, but the link name is not the same
224 		 * as the given device name.
225 		 */
226 		err = EEXIST;
227 		goto done;
228 	}
229 
230 	linkp->ll_media = media;
231 	linkp->ll_gen++;
232 
233 	(void) dlmgmt_write_db_entry(linkp->ll_linkid, linkp->ll_flags);
234 
235 done:
236 	dlmgmt_table_unlock();
237 	retvalp->lr_err = err;
238 }
239 
240 static void
241 dlmgmt_upcall_destroy(void *argp, void *retp)
242 {
243 	dlmgmt_upcall_arg_destroy_t	*destroy = argp;
244 	dlmgmt_destroy_retval_t		*retvalp = retp;
245 	datalink_id_t			linkid = destroy->ld_linkid;
246 	dlmgmt_link_t			*linkp = NULL;
247 	uint32_t			flags, dflags = 0;
248 	int				err = 0;
249 
250 	flags = DLMGMT_ACTIVE | (destroy->ld_persist ? DLMGMT_PERSIST : 0);
251 
252 	/*
253 	 * Hold the writer lock to update the link table.
254 	 */
255 	dlmgmt_table_lock(B_TRUE);
256 
257 	if ((linkp = link_by_id(linkid)) == NULL) {
258 		err = ENOENT;
259 		goto done;
260 	}
261 
262 	if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) &&
263 	    ((err = dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE)) != 0)) {
264 		dflags = DLMGMT_ACTIVE;
265 		goto done;
266 	}
267 
268 	if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) &&
269 	    ((err = dlmgmt_delete_db_entry(linkid, DLMGMT_PERSIST)) != 0)) {
270 		if (dflags != 0)
271 			(void) dlmgmt_write_db_entry(linkp->ll_linkid, dflags);
272 		dflags |= DLMGMT_PERSIST;
273 		goto done;
274 	}
275 
276 	if ((err = dlmgmt_destroy_common(linkp, flags)) != 0 && dflags != 0)
277 		(void) dlmgmt_write_db_entry(linkp->ll_linkid, dflags);
278 
279 done:
280 	dlmgmt_table_unlock();
281 	retvalp->lr_err = err;
282 }
283 
284 static void
285 dlmgmt_getname(void *argp, void *retp)
286 {
287 	dlmgmt_door_getname_t	*getname = argp;
288 	dlmgmt_getname_retval_t	*retvalp = retp;
289 	dlmgmt_link_t		*linkp;
290 	int			err = 0;
291 
292 	/*
293 	 * Hold the reader lock to access the link
294 	 */
295 	dlmgmt_table_lock(B_FALSE);
296 	if ((linkp = link_by_id(getname->ld_linkid)) == NULL) {
297 		/*
298 		 * The link does not exists.
299 		 */
300 		err = ENOENT;
301 		goto done;
302 	}
303 
304 	if (strlcpy(retvalp->lr_link, linkp->ll_link, MAXLINKNAMELEN) >=
305 	    MAXLINKNAMELEN) {
306 		err = ENOSPC;
307 		goto done;
308 	}
309 	retvalp->lr_flags = linkp->ll_flags;
310 	retvalp->lr_class = linkp->ll_class;
311 	retvalp->lr_media = linkp->ll_media;
312 
313 done:
314 	dlmgmt_table_unlock();
315 	retvalp->lr_err = err;
316 }
317 
318 static void
319 dlmgmt_getlinkid(void *argp, void *retp)
320 {
321 	dlmgmt_door_getlinkid_t	*getlinkid = argp;
322 	dlmgmt_getlinkid_retval_t *retvalp = retp;
323 	dlmgmt_link_t		*linkp;
324 	int			err = 0;
325 
326 	/*
327 	 * Hold the reader lock to access the link
328 	 */
329 	dlmgmt_table_lock(B_FALSE);
330 	if ((linkp = link_by_name(getlinkid->ld_link)) == NULL) {
331 		/*
332 		 * The link does not exists.
333 		 */
334 		err = ENOENT;
335 		goto done;
336 	}
337 
338 	retvalp->lr_linkid = linkp->ll_linkid;
339 	retvalp->lr_flags = linkp->ll_flags;
340 	retvalp->lr_class = linkp->ll_class;
341 	retvalp->lr_media = linkp->ll_media;
342 
343 done:
344 	dlmgmt_table_unlock();
345 	retvalp->lr_err = err;
346 }
347 
348 static void
349 dlmgmt_getnext(void *argp, void *retp)
350 {
351 	dlmgmt_door_getnext_t	*getnext = argp;
352 	dlmgmt_getnext_retval_t	*retvalp = retp;
353 	dlmgmt_link_t		link, *linkp;
354 	datalink_id_t		linkid = getnext->ld_linkid;
355 	avl_index_t		where;
356 	int			err = 0;
357 
358 	/*
359 	 * Hold the reader lock to access the link
360 	 */
361 	dlmgmt_table_lock(B_FALSE);
362 
363 	link.ll_linkid = (linkid + 1);
364 	linkp = avl_find(&dlmgmt_id_avl, &link, &where);
365 	if (linkp == NULL)
366 		linkp = avl_nearest(&dlmgmt_id_avl, where, AVL_AFTER);
367 
368 	for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
369 		if ((linkp->ll_class & getnext->ld_class) &&
370 		    (linkp->ll_flags & getnext->ld_flags) &&
371 		    DATALINK_MEDIA_ACCEPTED(getnext->ld_dmedia,
372 		    linkp->ll_media))
373 			break;
374 	}
375 
376 	if (linkp == NULL) {
377 		err = ENOENT;
378 	} else {
379 		retvalp->lr_linkid = linkp->ll_linkid;
380 		retvalp->lr_class = linkp->ll_class;
381 		retvalp->lr_media = linkp->ll_media;
382 		retvalp->lr_flags = linkp->ll_flags;
383 	}
384 
385 	dlmgmt_table_unlock();
386 	retvalp->lr_err = err;
387 }
388 
389 static void
390 dlmgmt_upcall_getattr(void *argp, void *retp)
391 {
392 	dlmgmt_upcall_arg_getattr_t	*getattr = argp;
393 	dlmgmt_getattr_retval_t		*retvalp = retp;
394 	dlmgmt_link_t			*linkp;
395 
396 	/*
397 	 * Hold the reader lock to access the link
398 	 */
399 	dlmgmt_table_lock(B_FALSE);
400 	if ((linkp = link_by_id(getattr->ld_linkid)) == NULL) {
401 		/*
402 		 * The link does not exist.
403 		 */
404 		retvalp->lr_err = ENOENT;
405 		goto done;
406 	}
407 
408 	dlmgmt_getattr_common(&linkp->ll_head, getattr->ld_attr, retvalp);
409 
410 done:
411 	dlmgmt_table_unlock();
412 }
413 
414 static void
415 dlmgmt_createid(void *argp, void *retp)
416 {
417 	dlmgmt_door_createid_t	*createid = argp;
418 	dlmgmt_createid_retval_t *retvalp = retp;
419 	dlmgmt_link_t		*linkp;
420 	datalink_id_t		linkid = DATALINK_INVALID_LINKID;
421 	char			link[MAXLINKNAMELEN];
422 	int			err;
423 
424 	/*
425 	 * Hold the writer lock to update the dlconf table.
426 	 */
427 	dlmgmt_table_lock(B_TRUE);
428 
429 	if (createid->ld_prefix) {
430 		err = dlmgmt_generate_name(createid->ld_link, link,
431 		    MAXLINKNAMELEN);
432 		if (err != 0)
433 			goto done;
434 
435 		err = dlmgmt_create_common(link, createid->ld_class,
436 		    createid->ld_media, createid->ld_flags, &linkp);
437 	} else {
438 		err = dlmgmt_create_common(createid->ld_link,
439 		    createid->ld_class, createid->ld_media, createid->ld_flags,
440 		    &linkp);
441 	}
442 
443 	if (err == 0) {
444 		/*
445 		 * Keep the active mapping.
446 		 */
447 		linkid = linkp->ll_linkid;
448 		if (createid->ld_flags & DLMGMT_ACTIVE)
449 			(void) dlmgmt_write_db_entry(linkid, DLMGMT_ACTIVE);
450 	}
451 
452 done:
453 	dlmgmt_table_unlock();
454 	retvalp->lr_linkid = linkid;
455 	retvalp->lr_err = err;
456 }
457 
458 static void
459 dlmgmt_destroyid(void *argp, void *retp)
460 {
461 	dlmgmt_door_destroyid_t	*destroyid = argp;
462 	dlmgmt_destroyid_retval_t *retvalp = retp;
463 	datalink_id_t		linkid = destroyid->ld_linkid;
464 	uint32_t		flags = destroyid->ld_flags;
465 	dlmgmt_link_t		*linkp = NULL;
466 	int			err = 0;
467 
468 	/*
469 	 * Hold the writer lock to update the link table.
470 	 */
471 	dlmgmt_table_lock(B_TRUE);
472 	if ((linkp = link_by_id(linkid)) == NULL) {
473 		err = ENOENT;
474 		goto done;
475 	}
476 
477 	if ((err = dlmgmt_destroy_common(linkp, flags)) != 0)
478 		goto done;
479 
480 	/*
481 	 * Delete the active mapping.
482 	 */
483 	if (flags & DLMGMT_ACTIVE)
484 		(void) dlmgmt_delete_db_entry(linkid, DLMGMT_ACTIVE);
485 
486 done:
487 	dlmgmt_table_unlock();
488 	retvalp->lr_err = err;
489 }
490 
491 /*
492  * Remap a linkid to a given link name, i.e., rename an existing link1
493  * (ld_linkid) to a non-existent link2 (ld_link): rename link1's name to
494  * the given link name.
495  */
496 static void
497 dlmgmt_remapid(void *argp, void *retp)
498 {
499 	dlmgmt_door_remapid_t	*remapid = argp;
500 	dlmgmt_remapid_retval_t	*retvalp = retp;
501 	datalink_id_t		linkid1 = remapid->ld_linkid;
502 	dlmgmt_link_t		link, *linkp1, *tmp;
503 	avl_index_t		where;
504 	int			err = 0;
505 
506 	if (!dladm_valid_linkname(remapid->ld_link)) {
507 		retvalp->lr_err = EINVAL;
508 		return;
509 	}
510 
511 	/*
512 	 * Hold the writer lock to update the link table.
513 	 */
514 	dlmgmt_table_lock(B_TRUE);
515 	if ((linkp1 = link_by_id(linkid1)) == NULL) {
516 		err = ENOENT;
517 		goto done;
518 	}
519 
520 	if (link_by_name(remapid->ld_link) != NULL) {
521 		err = EEXIST;
522 		goto done;
523 	}
524 
525 	avl_remove(&dlmgmt_name_avl, linkp1);
526 	(void) strlcpy(link.ll_link, remapid->ld_link, MAXLINKNAMELEN);
527 	tmp = avl_find(&dlmgmt_name_avl, &link, &where);
528 	assert(tmp == NULL);
529 	(void) strlcpy(linkp1->ll_link, remapid->ld_link, MAXLINKNAMELEN);
530 	avl_insert(&dlmgmt_name_avl, linkp1, where);
531 	dlmgmt_advance(linkp1);
532 
533 	/*
534 	 * If we renamed a temporary link, update the temporary repository.
535 	 */
536 	if (linkp1->ll_flags & DLMGMT_ACTIVE)
537 		(void) dlmgmt_write_db_entry(linkid1, DLMGMT_ACTIVE);
538 done:
539 	dlmgmt_table_unlock();
540 	retvalp->lr_err = err;
541 }
542 
543 static void
544 dlmgmt_upid(void *argp, void *retp)
545 {
546 	dlmgmt_door_upid_t	*upid = argp;
547 	dlmgmt_upid_retval_t	*retvalp = retp;
548 	dlmgmt_link_t		*linkp;
549 	int			err = 0;
550 
551 	/*
552 	 * Hold the writer lock to update the link table.
553 	 */
554 	dlmgmt_table_lock(B_TRUE);
555 	if ((linkp = link_by_id(upid->ld_linkid)) == NULL) {
556 		err = ENOENT;
557 		goto done;
558 	}
559 
560 	if (linkp->ll_flags & DLMGMT_ACTIVE) {
561 		err = EINVAL;
562 		goto done;
563 	}
564 
565 	linkp->ll_flags |= DLMGMT_ACTIVE;
566 	(void) dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_ACTIVE);
567 done:
568 	dlmgmt_table_unlock();
569 	retvalp->lr_err = err;
570 }
571 
572 static void
573 dlmgmt_createconf(void *argp, void *retp)
574 {
575 	dlmgmt_door_createconf_t *createconf = argp;
576 	dlmgmt_createconf_retval_t *retvalp = retp;
577 	dlmgmt_dlconf_t		dlconf, *dlconfp, *tmp;
578 	avl_index_t		where;
579 	int			err;
580 
581 	/*
582 	 * Hold the writer lock to update the dlconf table.
583 	 */
584 	dlmgmt_dlconf_table_lock(B_TRUE);
585 
586 	if ((err = dlconf_create(createconf->ld_link, createconf->ld_linkid,
587 	    createconf->ld_class, createconf->ld_media, &dlconfp)) != 0) {
588 		goto done;
589 	}
590 
591 	dlconf.ld_id = dlconfp->ld_id;
592 	tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where);
593 	assert(tmp == NULL);
594 	avl_insert(&dlmgmt_dlconf_avl, dlconfp, where);
595 	dlmgmt_advance_dlconfid(dlconfp);
596 
597 	retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id;
598 done:
599 	dlmgmt_dlconf_table_unlock();
600 	retvalp->lr_err = err;
601 }
602 
603 static void
604 dlmgmt_setattr(void *argp, void *retp)
605 {
606 	dlmgmt_door_setattr_t	*setattr = argp;
607 	dlmgmt_setattr_retval_t	*retvalp = retp;
608 	dlmgmt_dlconf_t		dlconf, *dlconfp;
609 	int			err = 0;
610 
611 	/*
612 	 * Hold the writer lock to update the dlconf table.
613 	 */
614 	dlmgmt_dlconf_table_lock(B_TRUE);
615 
616 	dlconf.ld_id = (int)setattr->ld_conf;
617 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
618 	if (dlconfp == NULL) {
619 		err = ENOENT;
620 		goto done;
621 	}
622 
623 	err = linkattr_set(&(dlconfp->ld_head), setattr->ld_attr,
624 	    &setattr->ld_attrval, setattr->ld_attrsz, setattr->ld_type);
625 
626 done:
627 	dlmgmt_dlconf_table_unlock();
628 	retvalp->lr_err = err;
629 }
630 
631 static void
632 dlmgmt_unsetconfattr(void *argp, void *retp)
633 {
634 	dlmgmt_door_unsetattr_t	*unsetattr = argp;
635 	dlmgmt_unsetattr_retval_t *retvalp = retp;
636 	dlmgmt_dlconf_t		dlconf, *dlconfp;
637 	int			err = 0;
638 
639 	/*
640 	 * Hold the writer lock to update the dlconf table.
641 	 */
642 	dlmgmt_dlconf_table_lock(B_TRUE);
643 
644 	dlconf.ld_id = (int)unsetattr->ld_conf;
645 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
646 	if (dlconfp == NULL) {
647 		err = ENOENT;
648 		goto done;
649 	}
650 
651 	err = linkattr_unset(&(dlconfp->ld_head), unsetattr->ld_attr);
652 
653 done:
654 	dlmgmt_dlconf_table_unlock();
655 	retvalp->lr_err = err;
656 }
657 
658 /*
659  * Note that dlmgmt_readconf() returns a conf ID of a conf AVL tree entry,
660  * which is managed by dlmgmtd.  The ID is used to find the conf entry when
661  * dlmgmt_write_conf() is called.  The conf entry contains an ld_gen value
662  * (which is the generation number - ll_gen) of the dlmgmt_link_t at the time
663  * of dlmgmt_readconf(), and ll_gen changes every time the dlmgmt_link_t
664  * changes its attributes.  Therefore, dlmgmt_write_conf() can compare ld_gen
665  * in the conf entry against the latest dlmgmt_link_t ll_gen value to see if
666  * anything has changed between the dlmgmt_read_conf() and dlmgmt_writeconf()
667  * calls.  If so, EAGAIN is returned.  This mechanism can ensures atomicity
668  * across the pair of dladm_read_conf() and dladm_write_conf() calls.
669  */
670 static void
671 dlmgmt_writeconf(void *argp, void *retp)
672 {
673 	dlmgmt_door_writeconf_t	*writeconf = argp;
674 	dlmgmt_writeconf_retval_t *retvalp = retp;
675 	dlmgmt_dlconf_t		dlconf, *dlconfp;
676 	dlmgmt_link_t		*linkp;
677 	dlmgmt_linkattr_t	*attrp, *next;
678 	int			err = 0;
679 
680 	/*
681 	 * Hold the read lock to access the dlconf table.
682 	 */
683 	dlmgmt_dlconf_table_lock(B_TRUE);
684 
685 	dlconf.ld_id = (int)writeconf->ld_conf;
686 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
687 	if (dlconfp == NULL) {
688 		err = ENOENT;
689 		goto done;
690 	}
691 
692 	/*
693 	 * Hold the writer lock to update the link table.
694 	 */
695 	dlmgmt_table_lock(B_TRUE);
696 	linkp = link_by_id(dlconfp->ld_linkid);
697 	if ((linkp == NULL) || (linkp->ll_class != dlconfp->ld_class) ||
698 	    (linkp->ll_media != dlconfp->ld_media) ||
699 	    (strcmp(linkp->ll_link, dlconfp->ld_link) != 0)) {
700 		/*
701 		 * The link does not exist.
702 		 */
703 		dlmgmt_table_unlock();
704 		err = ENOENT;
705 		goto done;
706 	}
707 
708 	if (linkp->ll_gen != dlconfp->ld_gen) {
709 		/*
710 		 * Something has changed the link configuration; try again.
711 		 */
712 		dlmgmt_table_unlock();
713 		err = EAGAIN;
714 		goto done;
715 	}
716 
717 	/*
718 	 * Delete the old attribute list.
719 	 */
720 	for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
721 		next = attrp->lp_next;
722 		free(attrp->lp_val);
723 		free(attrp);
724 	}
725 	linkp->ll_head = NULL;
726 
727 	/*
728 	 * Set the new attribute.
729 	 */
730 	for (attrp = dlconfp->ld_head; attrp != NULL; attrp = attrp->lp_next) {
731 		if ((err = linkattr_set(&(linkp->ll_head), attrp->lp_name,
732 		    attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) {
733 			dlmgmt_table_unlock();
734 			goto done;
735 		}
736 	}
737 
738 	linkp->ll_gen++;
739 	err = dlmgmt_write_db_entry(linkp->ll_linkid, DLMGMT_PERSIST);
740 	dlmgmt_table_unlock();
741 done:
742 	dlmgmt_dlconf_table_unlock();
743 	retvalp->lr_err = err;
744 }
745 
746 static void
747 dlmgmt_removeconf(void *argp, void *retp)
748 {
749 	dlmgmt_door_removeconf_t 	*removeconf = argp;
750 	dlmgmt_removeconf_retval_t	*retvalp = retp;
751 	int				err;
752 
753 	dlmgmt_table_lock(B_TRUE);
754 	err = dlmgmt_delete_db_entry(removeconf->ld_linkid, DLMGMT_PERSIST);
755 	dlmgmt_table_unlock();
756 	retvalp->lr_err = err;
757 }
758 
759 static void
760 dlmgmt_destroyconf(void *argp, void *retp)
761 {
762 	dlmgmt_door_destroyconf_t	*destroyconf = argp;
763 	dlmgmt_destroyconf_retval_t	*retvalp = retp;
764 	dlmgmt_dlconf_t			dlconf, *dlconfp;
765 	int				err = 0;
766 
767 	/*
768 	 * Hold the writer lock to update the dlconf table.
769 	 */
770 	dlmgmt_dlconf_table_lock(B_TRUE);
771 
772 	dlconf.ld_id = (int)destroyconf->ld_conf;
773 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
774 	if (dlconfp == NULL) {
775 		err = ENOENT;
776 		goto done;
777 	}
778 
779 	avl_remove(&dlmgmt_dlconf_avl, dlconfp);
780 	dlconf_destroy(dlconfp);
781 
782 done:
783 	dlmgmt_dlconf_table_unlock();
784 	retvalp->lr_err = err;
785 }
786 
787 /*
788  * See the comments above dladm_write_conf() to see how ld_gen is used to
789  * ensure atomicity across the {dlmgmt_readconf(), dlmgmt_writeconf()} pair.
790  */
791 static void
792 dlmgmt_readconf(void *argp, void *retp)
793 {
794 	dlmgmt_door_readconf_t	*readconf = argp;
795 	dlmgmt_readconf_retval_t *retvalp = retp;
796 	dlmgmt_link_t 		*linkp;
797 	datalink_id_t		linkid = readconf->ld_linkid;
798 	dlmgmt_dlconf_t		*dlconfp, *tmp, dlconf;
799 	dlmgmt_linkattr_t	*attrp;
800 	avl_index_t		where;
801 	int			err = 0;
802 
803 	/*
804 	 * Hold the writer lock to update the dlconf table.
805 	 */
806 	dlmgmt_dlconf_table_lock(B_TRUE);
807 
808 	/*
809 	 * Hold the reader lock to access the link
810 	 */
811 	dlmgmt_table_lock(B_FALSE);
812 	linkp = link_by_id(linkid);
813 	if ((linkp == NULL) || !(linkp->ll_flags & DLMGMT_PERSIST)) {
814 		/*
815 		 * The persistent link configuration does not exists.
816 		 */
817 		err = ENOENT;
818 		goto done;
819 	}
820 
821 	if ((err = dlconf_create(linkp->ll_link, linkp->ll_linkid,
822 	    linkp->ll_class, linkp->ll_media, &dlconfp)) != 0) {
823 		goto done;
824 	}
825 
826 	for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) {
827 		if ((err = linkattr_set(&(dlconfp->ld_head), attrp->lp_name,
828 		    attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) {
829 			dlconf_destroy(dlconfp);
830 			goto done;
831 		}
832 	}
833 	dlconfp->ld_gen = linkp->ll_gen;
834 
835 	dlconf.ld_id = dlconfp->ld_id;
836 	tmp = avl_find(&dlmgmt_dlconf_avl, &dlconf, &where);
837 	assert(tmp == NULL);
838 	avl_insert(&dlmgmt_dlconf_avl, dlconfp, where);
839 	dlmgmt_advance_dlconfid(dlconfp);
840 
841 	retvalp->lr_conf = (dladm_conf_t)dlconfp->ld_id;
842 done:
843 	dlmgmt_table_unlock();
844 	dlmgmt_dlconf_table_unlock();
845 	retvalp->lr_err = err;
846 }
847 
848 /*
849  * Note: the caller must free *retvalpp in case of success.
850  */
851 static void
852 dlmgmt_getattr(void *argp, void *retp)
853 {
854 	dlmgmt_door_getattr_t	*getattr = argp;
855 	dlmgmt_getattr_retval_t	*retvalp = retp;
856 	dlmgmt_dlconf_t		dlconf, *dlconfp;
857 
858 	/*
859 	 * Hold the read lock to access the dlconf table.
860 	 */
861 	dlmgmt_dlconf_table_lock(B_FALSE);
862 
863 	dlconf.ld_id = (int)getattr->ld_conf;
864 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
865 	if (dlconfp == NULL) {
866 		retvalp->lr_err = ENOENT;
867 		goto done;
868 	}
869 
870 	dlmgmt_getattr_common(&dlconfp->ld_head, getattr->ld_attr, retvalp);
871 
872 done:
873 	dlmgmt_dlconf_table_unlock();
874 }
875 
876 static void
877 dlmgmt_upcall_linkprop_init(void *argp, void *retp)
878 {
879 	dlmgmt_door_linkprop_init_t	*lip = argp;
880 	dlmgmt_linkprop_init_retval_t	*retvalp = retp;
881 	dlmgmt_link_t			*linkp;
882 	boolean_t			do_linkprop = B_FALSE;
883 
884 	/*
885 	 * Ignore wifi links until wifi property ioctls are converted
886 	 * to generic property ioctls. This avoids deadlocks due to
887 	 * wifi property ioctls using their own /dev/net device,
888 	 * not the DLD control device.
889 	 */
890 	dlmgmt_table_lock(B_FALSE);
891 	if ((linkp = link_by_id(lip->ld_linkid)) == NULL)
892 		retvalp->lr_err = ENOENT;
893 	else if (linkp->ll_media == DL_WIFI)
894 		retvalp->lr_err = 0;
895 	else
896 		do_linkprop = B_TRUE;
897 	dlmgmt_table_unlock();
898 
899 	if (do_linkprop)
900 		retvalp->lr_err = dladm_init_linkprop(lip->ld_linkid, B_TRUE);
901 }
902 
903 static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = {
904 	{ DLMGMT_CMD_DLS_CREATE, B_TRUE, sizeof (dlmgmt_upcall_arg_create_t),
905 	    sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create },
906 	{ DLMGMT_CMD_DLS_GETATTR, B_FALSE, sizeof (dlmgmt_upcall_arg_getattr_t),
907 	    sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr },
908 	{ DLMGMT_CMD_DLS_DESTROY, B_TRUE, sizeof (dlmgmt_upcall_arg_destroy_t),
909 	    sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy },
910 	{ DLMGMT_CMD_GETNAME, B_FALSE, sizeof (dlmgmt_door_getname_t),
911 	    sizeof (dlmgmt_getname_retval_t), dlmgmt_getname },
912 	{ DLMGMT_CMD_GETLINKID,	B_FALSE, sizeof (dlmgmt_door_getlinkid_t),
913 	    sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid },
914 	{ DLMGMT_CMD_GETNEXT, B_FALSE, sizeof (dlmgmt_door_getnext_t),
915 	    sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext },
916 	{ DLMGMT_CMD_DLS_UPDATE, B_TRUE, sizeof (dlmgmt_upcall_arg_update_t),
917 	    sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update },
918 	{ DLMGMT_CMD_CREATE_LINKID, B_TRUE, sizeof (dlmgmt_door_createid_t),
919 	    sizeof (dlmgmt_createid_retval_t), dlmgmt_createid },
920 	{ DLMGMT_CMD_DESTROY_LINKID, B_TRUE, sizeof (dlmgmt_door_destroyid_t),
921 	    sizeof (dlmgmt_destroyid_retval_t), dlmgmt_destroyid },
922 	{ DLMGMT_CMD_REMAP_LINKID, B_TRUE, sizeof (dlmgmt_door_remapid_t),
923 	    sizeof (dlmgmt_remapid_retval_t), dlmgmt_remapid },
924 	{ DLMGMT_CMD_CREATECONF, B_TRUE, sizeof (dlmgmt_door_createconf_t),
925 	    sizeof (dlmgmt_createconf_retval_t), dlmgmt_createconf },
926 	{ DLMGMT_CMD_READCONF, B_FALSE, sizeof (dlmgmt_door_readconf_t),
927 	    sizeof (dlmgmt_readconf_retval_t), dlmgmt_readconf },
928 	{ DLMGMT_CMD_WRITECONF, B_TRUE, sizeof (dlmgmt_door_writeconf_t),
929 	    sizeof (dlmgmt_writeconf_retval_t), dlmgmt_writeconf },
930 	{ DLMGMT_CMD_UP_LINKID, B_TRUE, sizeof (dlmgmt_door_upid_t),
931 	    sizeof (dlmgmt_upid_retval_t), dlmgmt_upid },
932 	{ DLMGMT_CMD_SETATTR, B_TRUE, sizeof (dlmgmt_door_setattr_t),
933 	    sizeof (dlmgmt_setattr_retval_t), dlmgmt_setattr },
934 	{ DLMGMT_CMD_UNSETATTR, B_TRUE, sizeof (dlmgmt_door_unsetattr_t),
935 	    sizeof (dlmgmt_unsetattr_retval_t), dlmgmt_unsetconfattr },
936 	{ DLMGMT_CMD_REMOVECONF, B_TRUE, sizeof (dlmgmt_door_removeconf_t),
937 	    sizeof (dlmgmt_removeconf_retval_t), dlmgmt_removeconf },
938 	{ DLMGMT_CMD_DESTROYCONF, B_TRUE, sizeof (dlmgmt_door_destroyconf_t),
939 	    sizeof (dlmgmt_destroyconf_retval_t), dlmgmt_destroyconf },
940 	{ DLMGMT_CMD_GETATTR, B_FALSE, sizeof (dlmgmt_door_getattr_t),
941 	    sizeof (dlmgmt_getattr_retval_t), dlmgmt_getattr },
942 	{ DLMGMT_CMD_LINKPROP_INIT, B_TRUE,
943 	    sizeof (dlmgmt_door_linkprop_init_t),
944 	    sizeof (dlmgmt_linkprop_init_retval_t),
945 	    dlmgmt_upcall_linkprop_init }
946 };
947 
948 #define	DLMGMT_INFO_TABLE_SIZE	(sizeof (i_dlmgmt_door_info_tbl) /	\
949     sizeof (i_dlmgmt_door_info_tbl[0]))
950 
951 /* ARGSUSED */
952 void
953 dlmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp,
954     uint_t n_desc)
955 {
956 	dlmgmt_door_info_t	*infop = NULL;
957 	dlmgmt_retval_t		retval;
958 	void			*retvalp;
959 	int			err;
960 	int			i;
961 
962 	for (i = 0; i < DLMGMT_INFO_TABLE_SIZE; i++) {
963 		if (i_dlmgmt_door_info_tbl[i].di_cmd ==
964 		    ((dlmgmt_door_arg_t *)(void *)argp)->ld_cmd) {
965 			infop = i_dlmgmt_door_info_tbl + i;
966 			break;
967 		}
968 	}
969 
970 	if (infop == NULL || argsz != infop->di_reqsz) {
971 		err = EINVAL;
972 		goto fail;
973 	}
974 
975 	if (infop->di_set) {
976 		ucred_t	*cred = NULL;
977 		const priv_set_t *eset;
978 
979 		if (door_ucred(&cred) != 0) {
980 			err = errno;
981 			goto fail;
982 		}
983 
984 		eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
985 		if (eset == NULL || !priv_ismember(eset, PRIV_SYS_NET_CONFIG)) {
986 			ucred_free(cred);
987 			err = EACCES;
988 			goto fail;
989 		}
990 	}
991 
992 	/*
993 	 * We cannot use malloc() here because door_return never returns, and
994 	 * memory allocated by malloc() would get leaked. Use alloca() instead.
995 	 */
996 	retvalp = alloca(infop->di_acksz);
997 	infop->di_handler(argp, retvalp);
998 	(void) door_return(retvalp, infop->di_acksz, NULL, 0);
999 	return;
1000 
1001 fail:
1002 	retval.lr_err = err;
1003 	(void) door_return((char *)&retval, sizeof (retval), NULL, 0);
1004 }
1005