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 
 
 |