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 (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 *);
  75 static void             dlmgmt_advance_ppa(dlmgmt_link_t *);
  76 
  77 void
  78 dlmgmt_log(int pri, const char *fmt, ...)
  79 {
  80         va_list alist;
  81 
  82         va_start(alist, fmt);
  83         if (debug) {
  84                 (void) vfprintf(stderr, fmt, alist);
  85                 (void) fputc('\n', stderr);
  86         } else {
  87                 vsyslog(pri, fmt, alist);
  88         }
  89         va_end(alist);
  90 }
  91 
  92 static int
  93 cmp_link_by_name(const void *v1, const void *v2)
  94 {
  95         const dlmgmt_link_t *link1 = v1;
  96         const dlmgmt_link_t *link2 = v2;
  97         int cmp;
  98 
  99         cmp = strcmp(link1->ll_link, link2->ll_link);
 100         return ((cmp == 0) ? 0 : ((cmp < 0) ? -1 : 1));
 101 }
 102 
 103 /*
 104  * Note that the zoneid associated with a link is effectively part of its
 105  * name.  This is essentially what results in having each zone have disjoint
 106  * datalink namespaces.
 107  */
 108 static int
 109 cmp_link_by_zname(const void *v1, const void *v2)
 110 {
 111         const dlmgmt_link_t *link1 = v1;
 112         const dlmgmt_link_t *link2 = v2;
 113 
 114         if (link1->ll_zoneid < link2->ll_zoneid)
 115                 return (-1);
 116         if (link1->ll_zoneid > link2->ll_zoneid)
 117                 return (1);
 118         return (cmp_link_by_name(link1, link2));
 119 }
 120 
 121 static int
 122 cmp_link_by_id(const void *v1, const void *v2)
 123 {
 124         const dlmgmt_link_t *link1 = v1;
 125         const dlmgmt_link_t *link2 = v2;
 126 
 127         if ((uint64_t)(link1->ll_linkid) == (uint64_t)(link2->ll_linkid))
 128                 return (0);
 129         else if ((uint64_t)(link1->ll_linkid) < (uint64_t)(link2->ll_linkid))
 130                 return (-1);
 131         else
 132                 return (1);
 133 }
 134 
 135 static int
 136 cmp_dlconf_by_id(const void *v1, const void *v2)
 137 {
 138         const dlmgmt_dlconf_t *dlconfp1 = v1;
 139         const dlmgmt_dlconf_t *dlconfp2 = v2;
 140 
 141         if (dlconfp1->ld_id == dlconfp2->ld_id)
 142                 return (0);
 143         else if (dlconfp1->ld_id < dlconfp2->ld_id)
 144                 return (-1);
 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 
 205         next = attrp->lp_next;
 206         prev = attrp->lp_prev;
 207         if (next != NULL)
 208                 next->lp_prev = prev;
 209         if (prev != NULL)
 210                 prev->lp_next = next;
 211         else
 212                 *headp = next;
 213 }
 214 
 215 dlmgmt_linkattr_t *
 216 linkattr_find(dlmgmt_linkattr_t *headp, const char *attr)
 217 {
 218         dlmgmt_linkattr_t *attrp;
 219 
 220         for (attrp = headp; attrp != NULL; attrp = attrp->lp_next) {
 221                 if (strcmp(attrp->lp_name, attr) == 0)
 222                         break;
 223         }
 224         return (attrp);
 225 }
 226 
 227 int
 228 linkattr_set(dlmgmt_linkattr_t **headp, const char *attr, void *attrval,
 229     size_t attrsz, dladm_datatype_t type)
 230 {
 231         dlmgmt_linkattr_t       *attrp;
 232         void                    *newval;
 233         boolean_t               new;
 234 
 235         attrp = linkattr_find(*headp, attr);
 236         if (attrp != NULL) {
 237                 /*
 238                  * It is already set.  If the value changed, update it.
 239                  */
 240                 if (linkattr_equal(headp, attr, attrval, attrsz))
 241                         return (0);
 242                 new = B_FALSE;
 243         } else {
 244                 /*
 245                  * It is not set yet, allocate the linkattr and prepend to the
 246                  * list.
 247                  */
 248                 if ((attrp = calloc(1, sizeof (dlmgmt_linkattr_t))) == NULL)
 249                         return (ENOMEM);
 250 
 251                 (void) strlcpy(attrp->lp_name, attr, MAXLINKATTRLEN);
 252                 new = B_TRUE;
 253         }
 254         if ((newval = calloc(1, attrsz)) == NULL) {
 255                 if (new)
 256                         free(attrp);
 257                 return (ENOMEM);
 258         }
 259 
 260         if (!new)
 261                 free(attrp->lp_val);
 262         attrp->lp_val = newval;
 263         bcopy(attrval, attrp->lp_val, attrsz);
 264         attrp->lp_sz = attrsz;
 265         attrp->lp_type = type;
 266         attrp->lp_linkprop = dladm_attr_is_linkprop(attr);
 267         if (new)
 268                 linkattr_add(headp, attrp);
 269         return (0);
 270 }
 271 
 272 void
 273 linkattr_unset(dlmgmt_linkattr_t **headp, const char *attr)
 274 {
 275         dlmgmt_linkattr_t *attrp;
 276 
 277         if ((attrp = linkattr_find(*headp, attr)) != NULL) {
 278                 linkattr_rm(headp, attrp);
 279                 free(attrp->lp_val);
 280                 free(attrp);
 281         }
 282 }
 283 
 284 int
 285 linkattr_get(dlmgmt_linkattr_t **headp, const char *attr, void **attrvalp,
 286     size_t *attrszp, dladm_datatype_t *typep)
 287 {
 288         dlmgmt_linkattr_t *attrp;
 289 
 290         if ((attrp = linkattr_find(*headp, attr)) == NULL)
 291                 return (ENOENT);
 292 
 293         *attrvalp = attrp->lp_val;
 294         *attrszp = attrp->lp_sz;
 295         if (typep != NULL)
 296                 *typep = attrp->lp_type;
 297         return (0);
 298 }
 299 
 300 boolean_t
 301 linkattr_equal(dlmgmt_linkattr_t **headp, const char *attr, void *attrval,
 302     size_t attrsz)
 303 {
 304         void    *saved_attrval;
 305         size_t  saved_attrsz;
 306 
 307         if (linkattr_get(headp, attr, &saved_attrval, &saved_attrsz, NULL) != 0)
 308                 return (B_FALSE);
 309 
 310         return ((saved_attrsz == attrsz) &&
 311             (memcmp(saved_attrval, attrval, attrsz) == 0));
 312 }
 313 
 314 void
 315 linkattr_destroy(dlmgmt_link_t *linkp)
 316 {
 317         dlmgmt_linkattr_t *next, *attrp;
 318 
 319         for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
 320                 next = attrp->lp_next;
 321                 free(attrp->lp_val);
 322                 free(attrp);
 323         }
 324 }
 325 
 326 static int
 327 dlmgmt_table_readwritelock(boolean_t write)
 328 {
 329         if (write)
 330                 return (pthread_rwlock_trywrlock(&dlmgmt_avl_lock));
 331         else
 332                 return (pthread_rwlock_tryrdlock(&dlmgmt_avl_lock));
 333 }
 334 
 335 void
 336 dlmgmt_table_lock(boolean_t write)
 337 {
 338         (void) pthread_mutex_lock(&dlmgmt_avl_mutex);
 339         while (dlmgmt_table_readwritelock(write) == EBUSY)
 340                 (void) pthread_cond_wait(&dlmgmt_avl_cv, &dlmgmt_avl_mutex);
 341 
 342         (void) pthread_mutex_unlock(&dlmgmt_avl_mutex);
 343 }
 344 
 345 void
 346 dlmgmt_table_unlock(void)
 347 {
 348         (void) pthread_rwlock_unlock(&dlmgmt_avl_lock);
 349         (void) pthread_mutex_lock(&dlmgmt_avl_mutex);
 350         (void) pthread_cond_broadcast(&dlmgmt_avl_cv);
 351         (void) pthread_mutex_unlock(&dlmgmt_avl_mutex);
 352 }
 353 
 354 void
 355 link_destroy(dlmgmt_link_t *linkp)
 356 {
 357         linkattr_destroy(linkp);
 358         free(linkp);
 359 }
 360 
 361 /*
 362  * Set the DLMGMT_ACTIVE flag on the link to note that it is active.  When a
 363  * link becomes active and it belongs to a non-global zone, it is also added
 364  * to that zone.
 365  */
 366 int
 367 link_activate(dlmgmt_link_t *linkp)
 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 ||
 409             (zoneid == GLOBAL_ZONEID && linkp->ll_onloan));
 410 }
 411 
 412 dlmgmt_link_t *
 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         linkp->ll_tomb = B_FALSE;
 465 
 466         if (avl_find(&dlmgmt_name_avl, linkp, &name_where) != NULL ||
 467             avl_find(&dlmgmt_id_avl, linkp, &id_where) != NULL) {
 468                 err = EEXIST;
 469                 goto done;
 470         }
 471 
 472         avl_insert(&dlmgmt_name_avl, linkp, name_where);
 473         avl_insert(&dlmgmt_id_avl, linkp, id_where);
 474 
 475         if ((flags & DLMGMT_ACTIVE) && (err = link_activate(linkp)) != 0) {
 476                 avl_remove(&dlmgmt_name_avl, linkp);
 477                 avl_remove(&dlmgmt_id_avl, linkp);
 478                 goto done;
 479         }
 480 
 481         linkp->ll_flags = flags;
 482         dlmgmt_advance(linkp);
 483         *linkpp = linkp;
 484 
 485 done:
 486         if (err != 0)
 487                 free(linkp);
 488         return (err);
 489 }
 490 
 491 int
 492 dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags)
 493 {
 494         if ((linkp->ll_flags & flags) == 0) {
 495                 /*
 496                  * The link does not exist in the specified space.
 497                  */
 498                 return (ENOENT);
 499         }
 500 
 501         linkp->ll_flags &= ~flags;
 502         if (flags & DLMGMT_PERSIST) {
 503                 dlmgmt_linkattr_t *next, *attrp;
 504 
 505                 for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
 506                         next = attrp->lp_next;
 507                         free(attrp->lp_val);
 508                         free(attrp);
 509                 }
 510                 linkp->ll_head = NULL;
 511         }
 512 
 513         if ((flags & DLMGMT_ACTIVE) && linkp->ll_zoneid != GLOBAL_ZONEID) {
 514                 (void) zone_remove_datalink(linkp->ll_zoneid, linkp->ll_linkid);
 515                 if (linkp->ll_onloan)
 516                         avl_remove(&dlmgmt_loan_avl, linkp);
 517         }
 518 
 519         if (linkp->ll_flags == 0) {
 520                 avl_remove(&dlmgmt_id_avl, linkp);
 521                 avl_remove(&dlmgmt_name_avl, linkp);
 522                 link_destroy(linkp);
 523         }
 524 
 525         return (0);
 526 }
 527 
 528 int
 529 dlmgmt_getattr_common(dlmgmt_linkattr_t **headp, const char *attr,
 530     dlmgmt_getattr_retval_t *retvalp)
 531 {
 532         int                     err;
 533         void                    *attrval;
 534         size_t                  attrsz;
 535         dladm_datatype_t        attrtype;
 536 
 537         err = linkattr_get(headp, attr, &attrval, &attrsz, &attrtype);
 538         if (err != 0)
 539                 return (err);
 540 
 541         assert(attrsz > 0);
 542         if (attrsz > MAXLINKATTRVALLEN)
 543                 return (EINVAL);
 544 
 545         retvalp->lr_type = attrtype;
 546         retvalp->lr_attrsz = attrsz;
 547         bcopy(attrval, retvalp->lr_attrval, attrsz);
 548         return (0);
 549 }
 550 
 551 void
 552 dlmgmt_dlconf_table_lock(boolean_t write)
 553 {
 554         if (write)
 555                 (void) pthread_rwlock_wrlock(&dlmgmt_dlconf_lock);
 556         else
 557                 (void) pthread_rwlock_rdlock(&dlmgmt_dlconf_lock);
 558 }
 559 
 560 void
 561 dlmgmt_dlconf_table_unlock(void)
 562 {
 563         (void) pthread_rwlock_unlock(&dlmgmt_dlconf_lock);
 564 }
 565 
 566 int
 567 dlconf_create(const char *name, datalink_id_t linkid, datalink_class_t class,
 568     uint32_t media, zoneid_t zoneid, dlmgmt_dlconf_t **dlconfpp)
 569 {
 570         dlmgmt_dlconf_t *dlconfp = NULL;
 571         int             err = 0;
 572 
 573         if (dlmgmt_nextconfid == 0) {
 574                 err = ENOSPC;
 575                 goto done;
 576         }
 577 
 578         if ((dlconfp = calloc(1, sizeof (dlmgmt_dlconf_t))) == NULL) {
 579                 err = ENOMEM;
 580                 goto done;
 581         }
 582 
 583         (void) strlcpy(dlconfp->ld_link, name, MAXLINKNAMELEN);
 584         dlconfp->ld_linkid = linkid;
 585         dlconfp->ld_class = class;
 586         dlconfp->ld_media = media;
 587         dlconfp->ld_id = dlmgmt_nextconfid;
 588         dlconfp->ld_zoneid = zoneid;
 589 
 590 done:
 591         *dlconfpp = dlconfp;
 592         return (err);
 593 }
 594 
 595 void
 596 dlconf_destroy(dlmgmt_dlconf_t *dlconfp)
 597 {
 598         dlmgmt_linkattr_t *next, *attrp;
 599 
 600         for (attrp = dlconfp->ld_head; attrp != NULL; attrp = next) {
 601                 next = attrp->lp_next;
 602                 free(attrp->lp_val);
 603                 free(attrp);
 604         }
 605         free(dlconfp);
 606 }
 607 
 608 int
 609 dlmgmt_generate_name(const char *prefix, char *name, size_t size,
 610     zoneid_t zoneid)
 611 {
 612         dlmgmt_prefix_t *lpp, *prev = NULL;
 613         dlmgmt_link_t   link, *linkp;
 614 
 615         /*
 616          * See whether the requested prefix is already in the list.
 617          */
 618         for (lpp = &dlmgmt_prefixlist; lpp != NULL;
 619             prev = lpp, lpp = lpp->lp_next) {
 620                 if (lpp->lp_zoneid == zoneid &&
 621                     strcmp(prefix, lpp->lp_prefix) == 0)
 622                         break;
 623         }
 624 
 625         /*
 626          * Not found.
 627          */
 628         if (lpp == NULL) {
 629                 assert(prev != NULL);
 630 
 631                 /*
 632                  * First add this new prefix into the prefix list.
 633                  */
 634                 if ((lpp = malloc(sizeof (dlmgmt_prefix_t))) == NULL)
 635                         return (ENOMEM);
 636 
 637                 prev->lp_next = lpp;
 638                 lpp->lp_next = NULL;
 639                 lpp->lp_zoneid = zoneid;
 640                 lpp->lp_nextppa = 0;
 641                 (void) strlcpy(lpp->lp_prefix, prefix, MAXLINKNAMELEN);
 642 
 643                 /*
 644                  * Now determine this prefix's nextppa.
 645                  */
 646                 (void) snprintf(link.ll_link, MAXLINKNAMELEN, "%s%d",
 647                     prefix, 0);
 648                 link.ll_zoneid = zoneid;
 649                 if ((linkp = avl_find(&dlmgmt_name_avl, &link, NULL)) != NULL)
 650                         dlmgmt_advance_ppa(linkp);
 651         }
 652 
 653         if (lpp->lp_nextppa == (uint_t)-1)
 654                 return (ENOSPC);
 655 
 656         (void) snprintf(name, size, "%s%d", prefix, lpp->lp_nextppa);
 657         return (0);
 658 }
 659 
 660 /*
 661  * Advance the next available ppa value if the name prefix of the current
 662  * link is in the prefix list.
 663  */
 664 static void
 665 dlmgmt_advance_ppa(dlmgmt_link_t *linkp)
 666 {
 667         dlmgmt_prefix_t *lpp;
 668         char            prefix[MAXLINKNAMELEN];
 669         char            linkname[MAXLINKNAMELEN];
 670         uint_t          start, ppa;
 671 
 672         (void) dlpi_parselink(linkp->ll_link, prefix, &ppa);
 673 
 674         /*
 675          * See whether the requested prefix is already in the list.
 676          */
 677         for (lpp = &dlmgmt_prefixlist; lpp != NULL; lpp = lpp->lp_next) {
 678                 if (lpp->lp_zoneid == linkp->ll_zoneid &&
 679                     strcmp(prefix, lpp->lp_prefix) == 0)
 680                         break;
 681         }
 682 
 683         /*
 684          * If the link name prefix is in the list, advance the
 685          * next available ppa for the <prefix>N name.
 686          */
 687         if (lpp == NULL || lpp->lp_nextppa != ppa)
 688                 return;
 689 
 690         start = lpp->lp_nextppa++;
 691         linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
 692         while (lpp->lp_nextppa != start) {
 693                 if (lpp->lp_nextppa == (uint_t)-1) {
 694                         /*
 695                          * wrapped around. search from <prefix>1.
 696                          */
 697                         lpp->lp_nextppa = 0;
 698                         (void) snprintf(linkname, MAXLINKNAMELEN,
 699                             "%s%d", lpp->lp_prefix, lpp->lp_nextppa);
 700                         linkp = link_by_name(linkname, lpp->lp_zoneid);
 701                         if (linkp == NULL)
 702                                 return;
 703                 } else {
 704                         if (linkp == NULL)
 705                                 return;
 706                         (void) dlpi_parselink(linkp->ll_link, prefix, &ppa);
 707                         if ((strcmp(prefix, lpp->lp_prefix) != 0) ||
 708                             (ppa != lpp->lp_nextppa)) {
 709                                 return;
 710                         }
 711                 }
 712                 linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
 713                 lpp->lp_nextppa++;
 714         }
 715         lpp->lp_nextppa = (uint_t)-1;
 716 }
 717 
 718 /*
 719  * Advance to the next available linkid value.
 720  */
 721 static void
 722 dlmgmt_advance_linkid(dlmgmt_link_t *linkp)
 723 {
 724         datalink_id_t   start;
 725 
 726         if (linkp->ll_linkid != dlmgmt_nextlinkid)
 727                 return;
 728 
 729         start = dlmgmt_nextlinkid;
 730         linkp = AVL_NEXT(&dlmgmt_id_avl, linkp);
 731 
 732         do {
 733                 if (dlmgmt_nextlinkid == DATALINK_MAX_LINKID) {
 734                         /*
 735                          * wrapped around. search from 1.
 736                          */
 737                         dlmgmt_nextlinkid = 1;
 738                         if ((linkp = link_by_id(1, GLOBAL_ZONEID)) == NULL)
 739                                 return;
 740                 } else {
 741                         dlmgmt_nextlinkid++;
 742                         if (linkp == NULL)
 743                                 return;
 744                         if (linkp->ll_linkid != dlmgmt_nextlinkid)
 745                                 return;
 746                 }
 747 
 748                 linkp = AVL_NEXT(&dlmgmt_id_avl, linkp);
 749         } while (dlmgmt_nextlinkid != start);
 750 
 751         dlmgmt_nextlinkid = DATALINK_INVALID_LINKID;
 752 }
 753 
 754 /*
 755  * Advance various global values, for example, next linkid value, next ppa for
 756  * various prefix etc.
 757  */
 758 void
 759 dlmgmt_advance(dlmgmt_link_t *linkp)
 760 {
 761         dlmgmt_advance_linkid(linkp);
 762         dlmgmt_advance_ppa(linkp);
 763 }
 764 
 765 /*
 766  * Advance to the next available dlconf id.
 767  */
 768 void
 769 dlmgmt_advance_dlconfid(dlmgmt_dlconf_t *dlconfp)
 770 {
 771         uint_t  start;
 772 
 773         start = dlmgmt_nextconfid++;
 774         dlconfp = AVL_NEXT(&dlmgmt_dlconf_avl, dlconfp);
 775         while (dlmgmt_nextconfid != start) {
 776                 if (dlmgmt_nextconfid == 0) {
 777                         dlmgmt_dlconf_t dlconf;
 778 
 779                         /*
 780                          * wrapped around. search from 1.
 781                          */
 782                         dlconf.ld_id = dlmgmt_nextconfid = 1;
 783                         dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
 784                         if (dlconfp == NULL)
 785                                 return;
 786                 } else {
 787                         if ((dlconfp == NULL) ||
 788                             (dlconfp->ld_id != dlmgmt_nextconfid)) {
 789                                 return;
 790                         }
 791                 }
 792                 dlconfp = AVL_NEXT(&dlmgmt_dlconf_avl, dlconfp);
 793                 dlmgmt_nextconfid++;
 794         }
 795         dlmgmt_nextconfid = 0;
 796 }