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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * Utility functions used by the dlmgmtd daemon.
28 */
29
30 #include <assert.h>
31 #include <pthread.h>
32 #include <stddef.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <strings.h>
37 #include <string.h>
38 #include <syslog.h>
39 #include <stdarg.h>
40 #include <zone.h>
41 #include <errno.h>
42 #include <libdlpi.h>
43 #include "dlmgmt_impl.h"
44
45 /*
46 * There are three datalink AVL tables. The dlmgmt_name_avl tree contains all
47 * datalinks and is keyed by zoneid and link name. The dlmgmt_id_avl also
48 * contains all datalinks, and it is keyed by link ID. The dlmgmt_loan_avl is
49 * keyed by link name, and contains the set of global-zone links that are
50 * currently on loan to non-global zones.
51 */
52 avl_tree_t dlmgmt_name_avl;
53 avl_tree_t dlmgmt_id_avl;
54 avl_tree_t dlmgmt_loan_avl;
55
56 avl_tree_t dlmgmt_dlconf_avl;
57
58 static pthread_rwlock_t dlmgmt_avl_lock = PTHREAD_RWLOCK_INITIALIZER;
59 static pthread_mutex_t dlmgmt_avl_mutex = PTHREAD_MUTEX_INITIALIZER;
60 static pthread_cond_t dlmgmt_avl_cv = PTHREAD_COND_INITIALIZER;
61 static pthread_rwlock_t dlmgmt_dlconf_lock = PTHREAD_RWLOCK_INITIALIZER;
62
63 typedef struct dlmgmt_prefix {
64 struct dlmgmt_prefix *lp_next;
65 char lp_prefix[MAXLINKNAMELEN];
66 zoneid_t lp_zoneid;
67 uint_t lp_nextppa;
68 } dlmgmt_prefix_t;
69 static dlmgmt_prefix_t dlmgmt_prefixlist;
70
71 datalink_id_t dlmgmt_nextlinkid;
72 static datalink_id_t dlmgmt_nextconfid = 1;
73
74 static void dlmgmt_advance_linkid(dlmgmt_link_t *);
145 else
146 return (1);
147 }
148
149 void
150 dlmgmt_linktable_init(void)
151 {
152 /*
153 * Initialize the prefix list. First add the "net" prefix for the
154 * global zone to the list.
155 */
156 dlmgmt_prefixlist.lp_next = NULL;
157 dlmgmt_prefixlist.lp_zoneid = GLOBAL_ZONEID;
158 dlmgmt_prefixlist.lp_nextppa = 0;
159 (void) strlcpy(dlmgmt_prefixlist.lp_prefix, "net", MAXLINKNAMELEN);
160
161 avl_create(&dlmgmt_name_avl, cmp_link_by_zname, sizeof (dlmgmt_link_t),
162 offsetof(dlmgmt_link_t, ll_name_node));
163 avl_create(&dlmgmt_id_avl, cmp_link_by_id, sizeof (dlmgmt_link_t),
164 offsetof(dlmgmt_link_t, ll_id_node));
165 avl_create(&dlmgmt_loan_avl, cmp_link_by_name, sizeof (dlmgmt_link_t),
166 offsetof(dlmgmt_link_t, ll_loan_node));
167 avl_create(&dlmgmt_dlconf_avl, cmp_dlconf_by_id,
168 sizeof (dlmgmt_dlconf_t), offsetof(dlmgmt_dlconf_t, ld_node));
169 dlmgmt_nextlinkid = 1;
170 }
171
172 void
173 dlmgmt_linktable_fini(void)
174 {
175 dlmgmt_prefix_t *lpp, *next;
176
177 for (lpp = dlmgmt_prefixlist.lp_next; lpp != NULL; lpp = next) {
178 next = lpp->lp_next;
179 free(lpp);
180 }
181
182 avl_destroy(&dlmgmt_dlconf_avl);
183 avl_destroy(&dlmgmt_name_avl);
184 avl_destroy(&dlmgmt_loan_avl);
185 avl_destroy(&dlmgmt_id_avl);
186 }
187
188 static void
189 linkattr_add(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp)
190 {
191 if (*headp == NULL) {
192 *headp = attrp;
193 } else {
194 (*headp)->lp_prev = attrp;
195 attrp->lp_next = *headp;
196 *headp = attrp;
197 }
198 }
199
200 static void
201 linkattr_rm(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp)
202 {
203 dlmgmt_linkattr_t *next, *prev;
204
368 {
369 int err = 0;
370 zoneid_t zoneid = ALL_ZONES;
371
372 if (zone_check_datalink(&zoneid, linkp->ll_linkid) == 0) {
373 /*
374 * This link was already added to a non-global zone. This can
375 * happen if dlmgmtd is restarted.
376 */
377 if (zoneid != linkp->ll_zoneid) {
378 if (link_by_name(linkp->ll_link, zoneid) != NULL) {
379 err = EEXIST;
380 goto done;
381 }
382
383 if (avl_find(&dlmgmt_name_avl, linkp, NULL) != NULL)
384 avl_remove(&dlmgmt_name_avl, linkp);
385
386 linkp->ll_zoneid = zoneid;
387 avl_add(&dlmgmt_name_avl, linkp);
388 avl_add(&dlmgmt_loan_avl, linkp);
389 linkp->ll_onloan = B_TRUE;
390 }
391 } else if (linkp->ll_zoneid != GLOBAL_ZONEID) {
392 err = zone_add_datalink(linkp->ll_zoneid, linkp->ll_linkid);
393 }
394 done:
395 if (err == 0)
396 linkp->ll_flags |= DLMGMT_ACTIVE;
397 return (err);
398 }
399
400 /*
401 * Is linkp visible from the caller's zoneid? It is if the link is in the
402 * same zone as the caller, or if the caller is in the global zone and the
403 * link is on loan to a non-global zone.
404 */
405 boolean_t
406 link_is_visible(dlmgmt_link_t *linkp, zoneid_t zoneid)
407 {
408 return (linkp->ll_zoneid == zoneid ||
413 link_by_id(datalink_id_t linkid, zoneid_t zoneid)
414 {
415 dlmgmt_link_t link, *linkp;
416
417 link.ll_linkid = linkid;
418 if ((linkp = avl_find(&dlmgmt_id_avl, &link, NULL)) == NULL)
419 return (NULL);
420 if (zoneid != GLOBAL_ZONEID && linkp->ll_zoneid != zoneid)
421 return (NULL);
422 return (linkp);
423 }
424
425 dlmgmt_link_t *
426 link_by_name(const char *name, zoneid_t zoneid)
427 {
428 dlmgmt_link_t link, *linkp;
429
430 (void) strlcpy(link.ll_link, name, MAXLINKNAMELEN);
431 link.ll_zoneid = zoneid;
432 linkp = avl_find(&dlmgmt_name_avl, &link, NULL);
433 if (linkp == NULL && zoneid == GLOBAL_ZONEID) {
434 /* The link could be on loan to a non-global zone? */
435 linkp = avl_find(&dlmgmt_loan_avl, &link, NULL);
436 }
437 return (linkp);
438 }
439
440 int
441 dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media,
442 zoneid_t zoneid, uint32_t flags, dlmgmt_link_t **linkpp)
443 {
444 dlmgmt_link_t *linkp = NULL;
445 avl_index_t name_where, id_where;
446 int err = 0;
447
448 if (!dladm_valid_linkname(name))
449 return (EINVAL);
450 if (dlmgmt_nextlinkid == DATALINK_INVALID_LINKID)
451 return (ENOSPC);
452
453 if ((linkp = calloc(1, sizeof (dlmgmt_link_t))) == NULL) {
454 err = ENOMEM;
455 goto done;
456 }
457
458 (void) strlcpy(linkp->ll_link, name, MAXLINKNAMELEN);
459 linkp->ll_class = class;
460 linkp->ll_media = media;
461 linkp->ll_linkid = dlmgmt_nextlinkid;
462 linkp->ll_zoneid = zoneid;
463 linkp->ll_gen = 0;
464
465 if (avl_find(&dlmgmt_name_avl, linkp, &name_where) != NULL ||
466 avl_find(&dlmgmt_id_avl, linkp, &id_where) != NULL) {
467 err = EEXIST;
468 goto done;
469 }
470
471 avl_insert(&dlmgmt_name_avl, linkp, name_where);
472 avl_insert(&dlmgmt_id_avl, linkp, id_where);
473
474 if ((flags & DLMGMT_ACTIVE) && (err = link_activate(linkp)) != 0) {
475 avl_remove(&dlmgmt_name_avl, linkp);
476 avl_remove(&dlmgmt_id_avl, linkp);
477 goto done;
478 }
479
480 linkp->ll_flags = flags;
481 dlmgmt_advance(linkp);
482 *linkpp = linkp;
483
494 /*
495 * The link does not exist in the specified space.
496 */
497 return (ENOENT);
498 }
499
500 linkp->ll_flags &= ~flags;
501 if (flags & DLMGMT_PERSIST) {
502 dlmgmt_linkattr_t *next, *attrp;
503
504 for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
505 next = attrp->lp_next;
506 free(attrp->lp_val);
507 free(attrp);
508 }
509 linkp->ll_head = NULL;
510 }
511
512 if ((flags & DLMGMT_ACTIVE) && linkp->ll_zoneid != GLOBAL_ZONEID) {
513 (void) zone_remove_datalink(linkp->ll_zoneid, linkp->ll_linkid);
514 if (linkp->ll_onloan)
515 avl_remove(&dlmgmt_loan_avl, linkp);
516 }
517
518 if (linkp->ll_flags == 0) {
519 avl_remove(&dlmgmt_id_avl, linkp);
520 avl_remove(&dlmgmt_name_avl, linkp);
521 link_destroy(linkp);
522 }
523
524 return (0);
525 }
526
527 int
528 dlmgmt_getattr_common(dlmgmt_linkattr_t **headp, const char *attr,
529 dlmgmt_getattr_retval_t *retvalp)
530 {
531 int err;
532 void *attrval;
533 size_t attrsz;
534 dladm_datatype_t attrtype;
535
|
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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2011, Joyent Inc. All rights reserved.
25 */
26
27 /*
28 * Utility functions used by the dlmgmtd daemon.
29 */
30
31 #include <assert.h>
32 #include <pthread.h>
33 #include <stddef.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <errno.h>
37 #include <strings.h>
38 #include <string.h>
39 #include <syslog.h>
40 #include <stdarg.h>
41 #include <zone.h>
42 #include <errno.h>
43 #include <libdlpi.h>
44 #include "dlmgmt_impl.h"
45
46 /*
47 * There are three datalink AVL tables. The dlmgmt_name_avl tree contains all
48 * datalinks and is keyed by zoneid and link name. The dlmgmt_id_avl also
49 * contains all datalinks, and it is keyed by link ID.
50 */
51 avl_tree_t dlmgmt_name_avl;
52 avl_tree_t dlmgmt_id_avl;
53
54 avl_tree_t dlmgmt_dlconf_avl;
55
56 static pthread_rwlock_t dlmgmt_avl_lock = PTHREAD_RWLOCK_INITIALIZER;
57 static pthread_mutex_t dlmgmt_avl_mutex = PTHREAD_MUTEX_INITIALIZER;
58 static pthread_cond_t dlmgmt_avl_cv = PTHREAD_COND_INITIALIZER;
59 static pthread_rwlock_t dlmgmt_dlconf_lock = PTHREAD_RWLOCK_INITIALIZER;
60
61 typedef struct dlmgmt_prefix {
62 struct dlmgmt_prefix *lp_next;
63 char lp_prefix[MAXLINKNAMELEN];
64 zoneid_t lp_zoneid;
65 uint_t lp_nextppa;
66 } dlmgmt_prefix_t;
67 static dlmgmt_prefix_t dlmgmt_prefixlist;
68
69 datalink_id_t dlmgmt_nextlinkid;
70 static datalink_id_t dlmgmt_nextconfid = 1;
71
72 static void dlmgmt_advance_linkid(dlmgmt_link_t *);
143 else
144 return (1);
145 }
146
147 void
148 dlmgmt_linktable_init(void)
149 {
150 /*
151 * Initialize the prefix list. First add the "net" prefix for the
152 * global zone to the list.
153 */
154 dlmgmt_prefixlist.lp_next = NULL;
155 dlmgmt_prefixlist.lp_zoneid = GLOBAL_ZONEID;
156 dlmgmt_prefixlist.lp_nextppa = 0;
157 (void) strlcpy(dlmgmt_prefixlist.lp_prefix, "net", MAXLINKNAMELEN);
158
159 avl_create(&dlmgmt_name_avl, cmp_link_by_zname, sizeof (dlmgmt_link_t),
160 offsetof(dlmgmt_link_t, ll_name_node));
161 avl_create(&dlmgmt_id_avl, cmp_link_by_id, sizeof (dlmgmt_link_t),
162 offsetof(dlmgmt_link_t, ll_id_node));
163 avl_create(&dlmgmt_dlconf_avl, cmp_dlconf_by_id,
164 sizeof (dlmgmt_dlconf_t), offsetof(dlmgmt_dlconf_t, ld_node));
165 dlmgmt_nextlinkid = 1;
166 }
167
168 void
169 dlmgmt_linktable_fini(void)
170 {
171 dlmgmt_prefix_t *lpp, *next;
172
173 for (lpp = dlmgmt_prefixlist.lp_next; lpp != NULL; lpp = next) {
174 next = lpp->lp_next;
175 free(lpp);
176 }
177
178 avl_destroy(&dlmgmt_dlconf_avl);
179 avl_destroy(&dlmgmt_name_avl);
180 avl_destroy(&dlmgmt_id_avl);
181 }
182
183 static void
184 linkattr_add(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp)
185 {
186 if (*headp == NULL) {
187 *headp = attrp;
188 } else {
189 (*headp)->lp_prev = attrp;
190 attrp->lp_next = *headp;
191 *headp = attrp;
192 }
193 }
194
195 static void
196 linkattr_rm(dlmgmt_linkattr_t **headp, dlmgmt_linkattr_t *attrp)
197 {
198 dlmgmt_linkattr_t *next, *prev;
199
363 {
364 int err = 0;
365 zoneid_t zoneid = ALL_ZONES;
366
367 if (zone_check_datalink(&zoneid, linkp->ll_linkid) == 0) {
368 /*
369 * This link was already added to a non-global zone. This can
370 * happen if dlmgmtd is restarted.
371 */
372 if (zoneid != linkp->ll_zoneid) {
373 if (link_by_name(linkp->ll_link, zoneid) != NULL) {
374 err = EEXIST;
375 goto done;
376 }
377
378 if (avl_find(&dlmgmt_name_avl, linkp, NULL) != NULL)
379 avl_remove(&dlmgmt_name_avl, linkp);
380
381 linkp->ll_zoneid = zoneid;
382 avl_add(&dlmgmt_name_avl, linkp);
383 linkp->ll_onloan = B_TRUE;
384 }
385 } else if (linkp->ll_zoneid != GLOBAL_ZONEID) {
386 err = zone_add_datalink(linkp->ll_zoneid, linkp->ll_linkid);
387 }
388 done:
389 if (err == 0)
390 linkp->ll_flags |= DLMGMT_ACTIVE;
391 return (err);
392 }
393
394 /*
395 * Is linkp visible from the caller's zoneid? It is if the link is in the
396 * same zone as the caller, or if the caller is in the global zone and the
397 * link is on loan to a non-global zone.
398 */
399 boolean_t
400 link_is_visible(dlmgmt_link_t *linkp, zoneid_t zoneid)
401 {
402 return (linkp->ll_zoneid == zoneid ||
407 link_by_id(datalink_id_t linkid, zoneid_t zoneid)
408 {
409 dlmgmt_link_t link, *linkp;
410
411 link.ll_linkid = linkid;
412 if ((linkp = avl_find(&dlmgmt_id_avl, &link, NULL)) == NULL)
413 return (NULL);
414 if (zoneid != GLOBAL_ZONEID && linkp->ll_zoneid != zoneid)
415 return (NULL);
416 return (linkp);
417 }
418
419 dlmgmt_link_t *
420 link_by_name(const char *name, zoneid_t zoneid)
421 {
422 dlmgmt_link_t link, *linkp;
423
424 (void) strlcpy(link.ll_link, name, MAXLINKNAMELEN);
425 link.ll_zoneid = zoneid;
426 linkp = avl_find(&dlmgmt_name_avl, &link, NULL);
427 return (linkp);
428 }
429
430 int
431 dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media,
432 zoneid_t zoneid, uint32_t flags, dlmgmt_link_t **linkpp)
433 {
434 dlmgmt_link_t *linkp = NULL;
435 avl_index_t name_where, id_where;
436 int err = 0;
437
438 if (!dladm_valid_linkname(name))
439 return (EINVAL);
440 if (dlmgmt_nextlinkid == DATALINK_INVALID_LINKID)
441 return (ENOSPC);
442
443 if ((linkp = calloc(1, sizeof (dlmgmt_link_t))) == NULL) {
444 err = ENOMEM;
445 goto done;
446 }
447
448 (void) strlcpy(linkp->ll_link, name, MAXLINKNAMELEN);
449 linkp->ll_class = class;
450 linkp->ll_media = media;
451 linkp->ll_linkid = dlmgmt_nextlinkid;
452 linkp->ll_zoneid = zoneid;
453 linkp->ll_gen = 0;
454 linkp->ll_tomb = B_FALSE;
455
456 if (avl_find(&dlmgmt_name_avl, linkp, &name_where) != NULL ||
457 avl_find(&dlmgmt_id_avl, linkp, &id_where) != NULL) {
458 err = EEXIST;
459 goto done;
460 }
461
462 avl_insert(&dlmgmt_name_avl, linkp, name_where);
463 avl_insert(&dlmgmt_id_avl, linkp, id_where);
464
465 if ((flags & DLMGMT_ACTIVE) && (err = link_activate(linkp)) != 0) {
466 avl_remove(&dlmgmt_name_avl, linkp);
467 avl_remove(&dlmgmt_id_avl, linkp);
468 goto done;
469 }
470
471 linkp->ll_flags = flags;
472 dlmgmt_advance(linkp);
473 *linkpp = linkp;
474
485 /*
486 * The link does not exist in the specified space.
487 */
488 return (ENOENT);
489 }
490
491 linkp->ll_flags &= ~flags;
492 if (flags & DLMGMT_PERSIST) {
493 dlmgmt_linkattr_t *next, *attrp;
494
495 for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
496 next = attrp->lp_next;
497 free(attrp->lp_val);
498 free(attrp);
499 }
500 linkp->ll_head = NULL;
501 }
502
503 if ((flags & DLMGMT_ACTIVE) && linkp->ll_zoneid != GLOBAL_ZONEID) {
504 (void) zone_remove_datalink(linkp->ll_zoneid, linkp->ll_linkid);
505 }
506
507 if (linkp->ll_flags == 0) {
508 avl_remove(&dlmgmt_id_avl, linkp);
509 avl_remove(&dlmgmt_name_avl, linkp);
510 link_destroy(linkp);
511 }
512
513 return (0);
514 }
515
516 int
517 dlmgmt_getattr_common(dlmgmt_linkattr_t **headp, const char *attr,
518 dlmgmt_getattr_retval_t *retvalp)
519 {
520 int err;
521 void *attrval;
522 size_t attrsz;
523 dladm_datatype_t attrtype;
524
|